From f3dd51ab97cecdde671631e9f00c19d87b3f3aef Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Tue, 26 Apr 2016 16:35:46 -0700 Subject: [PATCH 1/4] Alter layout engine to conform closer to W3C spec The primary goals of this change are: - Better conformance to the W3C flexbox standard (https://www.w3.org/TR/css-flexbox-1/) and a clear articulation of the areas where it deviates from the spec. - Support for flex-shrink. - Conformance with layout effects of "overflow: hidden". Specifically, here are the limitations of this implementation as compared to the W3C flexbox standard (this is also documented in Layout.js): - Display property is always assumed to be 'flex' except for Text nodes, which are assumed to be 'inline-flex'. - The 'zIndex' property (or any form of z ordering) is not supported. Nodes are stacked in document order. - The 'order' property is not supported. The order of flex items is always defined by document order. - The 'visibility' property is always assumed to be 'visible'. Values of 'collapse' and 'hidden' are not supported. - The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The rarely-used 'wrap-reverse' is not supported. - Rather than allowing arbitrary combinations of flexGrow, flexShrink and flexBasis, this algorithm supports only the three most common combinations: - flex: 0 is equiavlent to flex: 0 0 auto - flex: n (where n is a positive value) is equivalent to flex: n 0 0 - flex: -1 (or any negative value) is equivalent to flex: 0 1 auto - Margins cannot be specified as 'auto'. They must be specified in terms of pixel values, and the default value is 0. - The 'baseline' value is not supported for alignItems and alignSelf properties. - Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be specified as pixel values, not as percentages. - There is no support for calculation of dimensions based on intrinsic aspect ratios (e.g. images). - There is no support for forced breaks. - It does not support vertical inline directions (top-to-bottom or bottom-to-top text). And here is how the implementation deviates from the standard (this is also documented in Layout.js): - Section 4.5 of the spec indicates that all flex items have a default minimum main size. For text blocks, for example, this is the width of the widest word. Calculating the minimum width is expensive, so we forego it and assume a default minimum main size of 0. - Min/Max sizes in the main axis are not honored when resolving flexible lengths. - The spec indicates that the default value for 'flexDirection' is 'row', but the algorithm below assumes a default of 'column'. --- README.md | 12 +- TestResult.xml | 9 - dist/css-layout.h | 1800 ++++++++++------- dist/css-layout.jar | Bin 15067 -> 16191 bytes dist/css-layout.js | 1633 +++++++++------ dist/css-layout.min.js | 2 +- dist/css-layout.min.js.map | 2 +- src/CSharpTranspiler.js | 35 +- src/JavaTranspiler.js | 33 +- src/Layout-test-utils.js | 49 +- src/Layout.c | 1748 +++++++++------- src/Layout.h | 52 +- src/Layout.js | 1633 +++++++++------ src/__tests__/Layout-test.c | 1044 +++++++++- src/__tests__/Layout-test.js | 309 ++- .../LayoutEngineTest.cs | 1086 +++++++++- src/csharp/Facebook.CSSLayout/Assertions.cs | 5 + .../CSSCachedMeasurement.cs | 22 + src/csharp/Facebook.CSSLayout/CSSLayout.cs | 46 +- .../Facebook.CSSLayout/CSSLayoutContext.cs | 1 + .../Facebook.CSSLayout/CSSMeasureMode.cs | 2 +- src/csharp/Facebook.CSSLayout/CSSNode.cs | 4 +- src/csharp/Facebook.CSSLayout/CSSOverflow.cs | 17 + src/csharp/Facebook.CSSLayout/CSSStyle.cs | 1 + .../Facebook.CSSLayout.csproj | 6 +- src/csharp/Facebook.CSSLayout/LayoutEngine.cs | 1588 +++++++++------ .../csslayout/CSSCachedMeasurement.java | 19 + .../src/com/facebook/csslayout/CSSLayout.java | 46 +- .../facebook/csslayout/CSSLayoutContext.java | 1 + .../src/com/facebook/csslayout/CSSNode.java | 6 +- .../com/facebook/csslayout/CSSOverflow.java | 14 + .../src/com/facebook/csslayout/CSSStyle.java | 2 + .../com/facebook/csslayout/LayoutEngine.java | 1591 +++++++++------ .../facebook/csslayout/LayoutCachingTest.java | 2 +- .../facebook/csslayout/LayoutEngineTest.java | 1080 +++++++++- src/transpile.js | 46 +- 36 files changed, 9789 insertions(+), 4157 deletions(-) delete mode 100644 TestResult.xml create mode 100644 src/csharp/Facebook.CSSLayout/CSSCachedMeasurement.cs create mode 100644 src/csharp/Facebook.CSSLayout/CSSOverflow.cs create mode 100644 src/java/src/com/facebook/csslayout/CSSCachedMeasurement.java create mode 100644 src/java/src/com/facebook/csslayout/CSSOverflow.java diff --git a/README.md b/README.md index e3817e7a..607295e8 100644 --- a/README.md +++ b/README.md @@ -92,10 +92,19 @@ borderWidth, borderLeftWidth, borderRightWidth, borderTopWidth, borderBottomWidt flexDirection | 'column', 'row' justifyContent | 'flex-start', 'center', 'flex-end', 'space-between', 'space-around' alignItems, alignSelf | 'flex-start', 'center', 'flex-end', 'stretch' -flex | positive number +flex | number flexWrap | 'wrap', 'nowrap' position | 'relative', 'absolute' +overflow | 'visible', 'hidden' +- Rather than allowing arbitrary combinations of `flex-grow`, `flex-shrink`, and `flex-basis` the implementation only supports a few common combinations expressed as a single number using the `flex` attribute: + + css-layout `flex` value | W3C `flex` short-hand equivalent + ---|--- + n (where n > 0) | n 0 0 + 0 | 0 0 auto + -1 | 0 1 auto + - `inherit` value is not implemented because it's a way to disambiguate between multiple colliding rules. This should be done in a pre-processing step, not in the actual layout algorithm. @@ -118,6 +127,7 @@ div, span { border: 0 solid black; margin: 0; padding: 0; + min-width: 0; } ``` diff --git a/TestResult.xml b/TestResult.xml deleted file mode 100644 index 77989325..00000000 --- a/TestResult.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/dist/css-layout.h b/dist/css-layout.h index 5cf50521..95eab64d 100644 --- a/dist/css-layout.h +++ b/dist/css-layout.h @@ -48,6 +48,11 @@ typedef enum { CSS_JUSTIFY_SPACE_AROUND } css_justify_t; +typedef enum { + CSS_OVERFLOW_VISIBLE = 0, + CSS_OVERFLOW_HIDDEN +} css_overflow_t; + // Note: auto is only a valid value for alignSelf. It is NOT a valid value for // alignItems. typedef enum { @@ -83,7 +88,8 @@ typedef enum { typedef enum { CSS_MEASURE_MODE_UNDEFINED = 0, CSS_MEASURE_MODE_EXACTLY, - CSS_MEASURE_MODE_AT_MOST + CSS_MEASURE_MODE_AT_MOST, + CSS_MEASURE_MODE_COUNT } css_measure_mode_t; typedef enum { @@ -91,20 +97,40 @@ typedef enum { CSS_HEIGHT } css_dimension_t; +typedef struct { + float available_width; + float available_height; + css_measure_mode_t width_measure_mode; + css_measure_mode_t height_measure_mode; + + float computed_width; + float computed_height; +} css_cached_measurement_t; + +enum { + // This value was chosen based on empiracle data. Even the most complicated + // layouts should not require more than 16 entries to fit within the cache. + CSS_MAX_CACHED_RESULT_COUNT = 16 +}; + typedef struct { float position[4]; float dimensions[2]; css_direction_t direction; + float flex_basis; + // Instead of recomputing the entire layout every single time, we // cache some information to break early when nothing changed bool should_update; - float last_requested_dimensions[2]; - float last_parent_max_width; - float last_parent_max_height; - float last_dimensions[2]; - float last_position[2]; - css_direction_t last_direction; + int generation_count; + css_direction_t last_parent_direction; + + int next_cached_measurements_index; + css_cached_measurement_t cached_measurements[CSS_MAX_CACHED_RESULT_COUNT]; + float measured_dimensions[2]; + + css_cached_measurement_t cached_layout; } css_layout_t; typedef struct { @@ -120,6 +146,7 @@ typedef struct { css_align_t align_self; css_position_type_t position_type; css_wrap_type_t flex_wrap; + css_overflow_t overflow; float flex; float margin[6]; float position[4]; @@ -147,8 +174,7 @@ struct css_node { int children_count; int line_index; - css_node_t *next_absolute_child; - css_node_t *next_flex_child; + css_node_t* next_child; css_dim_t (*measure)(void *context, float width, css_measure_mode_t widthMode, float height, css_measure_mode_t heightMode); void (*print)(void *context); @@ -170,13 +196,9 @@ typedef enum { } css_print_options_t; void print_css_node(css_node_t *node, css_print_options_t options); -bool isUndefined(float value); - // Function that computes the layout! -void layoutNode(css_node_t *node, float maxWidth, float maxHeight, css_direction_t parentDirection); - -// Reset the calculated layout values for a given node. You should call this before `layoutNode`. -void resetNodeLayout(css_node_t *node); +void layoutNode(css_node_t *node, float availableWidth, float availableHeight, css_direction_t parentDirection); +bool isUndefined(float value); #endif @@ -190,6 +212,7 @@ void resetNodeLayout(css_node_t *node); * of patent rights can be found in the PATENTS file in the same directory. */ +#include #include #include #include @@ -212,6 +235,13 @@ __forceinline const float fmaxf(const float a, const float b) { #endif #endif +#define POSITIVE_FLEX_IS_AUTO 0 + +int gCurrentGenerationCount = 0; + +bool layoutNodeInternal(css_node_t* node, float availableWidth, float availableHeight, css_direction_t parentDirection, + css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, bool performLayout, char* reason); + bool isUndefined(float value) { return isnan(value); } @@ -223,12 +253,14 @@ static bool eq(float a, float b) { return fabs(a - b) < 0.0001; } -void init_css_node(css_node_t *node) { +void init_css_node(css_node_t* node) { node->style.align_items = CSS_ALIGN_STRETCH; node->style.align_content = CSS_ALIGN_FLEX_START; node->style.direction = CSS_DIRECTION_INHERIT; node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN; + + node->style.overflow = CSS_OVERFLOW_VISIBLE; // Some of the fields default to undefined and not 0 node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; @@ -256,21 +288,23 @@ void init_css_node(css_node_t *node) { node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; // Such that the comparison is always going to be false - node->layout.last_requested_dimensions[CSS_WIDTH] = -1; - node->layout.last_requested_dimensions[CSS_HEIGHT] = -1; - node->layout.last_parent_max_width = -1; - node->layout.last_parent_max_height = -1; - node->layout.last_direction = (css_direction_t)-1; + node->layout.last_parent_direction = (css_direction_t)-1; node->layout.should_update = true; + node->layout.next_cached_measurements_index = 0; + + node->layout.measured_dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->layout.measured_dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->layout.cached_layout.width_measure_mode = (css_measure_mode_t)-1; + node->layout.cached_layout.height_measure_mode = (css_measure_mode_t)-1; } -css_node_t *new_css_node() { - css_node_t *node = (css_node_t *)calloc(1, sizeof(*node)); +css_node_t* new_css_node() { + css_node_t* node = (css_node_t*)calloc(1, sizeof(*node)); init_css_node(node); return node; } -void free_css_node(css_node_t *node) { +void free_css_node(css_node_t* node) { free(node); } @@ -280,13 +314,13 @@ static void indent(int n) { } } -static void print_number_0(const char *str, float number) { +static void print_number_0(const char* str, float number) { if (!eq(number, 0)) { printf("%s: %g, ", str, number); } } -static void print_number_nan(const char *str, float number) { +static void print_number_nan(const char* str, float number) { if (!isnan(number)) { printf("%s: %g, ", str, number); } @@ -301,7 +335,7 @@ static bool four_equal(float four[4]) { static void print_css_node_rec( - css_node_t *node, + css_node_t* node, css_print_options_t options, int level ) { @@ -325,11 +359,11 @@ static void print_css_node_rec( if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) { printf("flexDirection: 'column', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { - printf("flexDirection: 'columnReverse', "); + printf("flexDirection: 'column-reverse', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) { printf("flexDirection: 'row', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { - printf("flexDirection: 'rowReverse', "); + printf("flexDirection: 'row-reverse', "); } if (node->style.justify_content == CSS_JUSTIFY_CENTER) { @@ -370,6 +404,12 @@ static void print_css_node_rec( print_number_nan("flex", node->style.flex); + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + printf("overflow: 'hidden', "); + } else if (node->style.overflow == CSS_OVERFLOW_VISIBLE) { + printf("overflow: 'visible', "); + } + if (four_equal(node->style.margin)) { print_number_0("margin", node->style.margin[CSS_LEFT]); } else { @@ -382,7 +422,7 @@ static void print_css_node_rec( } if (four_equal(node->style.padding)) { - print_number_0("padding", node->style.margin[CSS_LEFT]); + print_number_0("padding", node->style.padding[CSS_LEFT]); } else { print_number_0("paddingLeft", node->style.padding[CSS_LEFT]); print_number_0("paddingRight", node->style.padding[CSS_RIGHT]); @@ -405,6 +445,10 @@ static void print_css_node_rec( print_number_nan("width", node->style.dimensions[CSS_WIDTH]); print_number_nan("height", node->style.dimensions[CSS_HEIGHT]); + print_number_nan("maxWidth", node->style.maxDimensions[CSS_WIDTH]); + print_number_nan("maxHeight", node->style.maxDimensions[CSS_HEIGHT]); + print_number_nan("minWidth", node->style.minDimensions[CSS_WIDTH]); + print_number_nan("minHeight", node->style.minDimensions[CSS_HEIGHT]); if (node->style.position_type == CSS_POSITION_ABSOLUTE) { printf("position: 'absolute', "); @@ -428,11 +472,10 @@ static void print_css_node_rec( } } -void print_css_node(css_node_t *node, css_print_options_t options) { +void print_css_node(css_node_t* node, css_print_options_t options) { print_css_node_rec(node, options, 0); } - static css_position_t leading[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, @@ -468,7 +511,41 @@ static bool isColumnDirection(css_flex_direction_t flex_direction) { flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE; } -static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { +static bool isFlexBasisAuto(css_node_t* node) { +#if POSITIVE_FLEX_IS_AUTO + // All flex values are auto. + (void) node; + return true; +#else + // A flex value > 0 implies a basis of zero. + return node->style.flex <= 0; +#endif +} + +static float getFlexGrowFactor(css_node_t* node) { + // Flex grow is implied by positive values for flex. + if (node->style.flex > 0) { + return node->style.flex; + } + return 0; +} + +static float getFlexShrinkFactor(css_node_t* node) { +#if POSITIVE_FLEX_IS_AUTO + // A flex shrink factor of 1 is implied by non-zero values for flex. + if (node->style.flex != 0) { + return 1; + } +#else + // A flex shrink factor of 1 is implied by negative values for flex. + if (node->style.flex < 0) { + return 1; + } +#endif + return 0; +} + +static float getLeadingMargin(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) { return node->style.margin[CSS_START]; } @@ -476,7 +553,7 @@ static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { return node->style.margin[leading[axis]]; } -static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingMargin(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) { return node->style.margin[CSS_END]; } @@ -484,7 +561,7 @@ static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { return node->style.margin[trailing[axis]]; } -static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { +static float getLeadingPadding(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.padding[CSS_START]) && node->style.padding[CSS_START] >= 0) { @@ -498,7 +575,7 @@ static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingPadding(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.padding[CSS_END]) && node->style.padding[CSS_END] >= 0) { @@ -512,7 +589,7 @@ static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { +static float getLeadingBorder(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.border[CSS_START]) && node->style.border[CSS_START] >= 0) { @@ -526,7 +603,7 @@ static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingBorder(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.border[CSS_END]) && node->style.border[CSS_END] >= 0) { @@ -540,34 +617,30 @@ static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { +static float getLeadingPaddingAndBorder(css_node_t* node, css_flex_direction_t axis) { return getLeadingPadding(node, axis) + getLeadingBorder(node, axis); } -static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingPaddingAndBorder(css_node_t* node, css_flex_direction_t axis) { return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); } -static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) { - return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); -} - -static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) { +static float getMarginAxis(css_node_t* node, css_flex_direction_t axis) { return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } -static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) { +static float getPaddingAndBorderAxis(css_node_t* node, css_flex_direction_t axis) { return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis); } -static css_align_t getAlignItem(css_node_t *node, css_node_t *child) { +static css_align_t getAlignItem(css_node_t* node, css_node_t* child) { if (child->style.align_self != CSS_ALIGN_AUTO) { return child->style.align_self; } return node->style.align_items; } -static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) { +static css_direction_t resolveDirection(css_node_t* node, css_direction_t parentDirection) { css_direction_t direction = node->style.direction; if (direction == CSS_DIRECTION_INHERIT) { @@ -577,7 +650,7 @@ static css_direction_t resolveDirection(css_node_t *node, css_direction_t parent return direction; } -static css_flex_direction_t getFlexDirection(css_node_t *node) { +static css_flex_direction_t getFlexDirection(css_node_t* node) { return node->style.flex_direction; } @@ -601,46 +674,46 @@ static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_dire } } -static float getFlex(css_node_t *node) { +static float getFlex(css_node_t* node) { return node->style.flex; } -static bool isFlex(css_node_t *node) { +static bool isFlex(css_node_t* node) { return ( node->style.position_type == CSS_POSITION_RELATIVE && - getFlex(node) > 0 + getFlex(node) != 0 ); } -static bool isFlexWrap(css_node_t *node) { +static bool isFlexWrap(css_node_t* node) { return node->style.flex_wrap == CSS_WRAP; } -static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) { - return node->layout.dimensions[dim[axis]] + +static float getDimWithMargin(css_node_t* node, css_flex_direction_t axis) { + return node->layout.measured_dimensions[dim[axis]] + getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } -static bool isStyleDimDefined(css_node_t *node, css_flex_direction_t axis) { +static bool isStyleDimDefined(css_node_t* node, css_flex_direction_t axis) { float value = node->style.dimensions[dim[axis]]; return !isUndefined(value) && value >= 0.0; } -static bool isLayoutDimDefined(css_node_t *node, css_flex_direction_t axis) { - float value = node->layout.dimensions[dim[axis]]; +static bool isLayoutDimDefined(css_node_t* node, css_flex_direction_t axis) { + float value = node->layout.measured_dimensions[dim[axis]]; return !isUndefined(value) && value >= 0.0; } -static bool isPosDefined(css_node_t *node, css_position_t position) { +static bool isPosDefined(css_node_t* node, css_position_t position) { return !isUndefined(node->style.position[position]); } -static bool isMeasureDefined(css_node_t *node) { +static bool isMeasureDefined(css_node_t* node) { return node->measure; } -static float getPosition(css_node_t *node, css_position_t position) { +static float getPosition(css_node_t* node, css_position_t position) { float result = node->style.position[position]; if (!isUndefined(result)) { return result; @@ -648,7 +721,7 @@ static float getPosition(css_node_t *node, css_position_t position) { return 0; } -static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) { +static float boundAxisWithinMinAndMax(css_node_t* node, css_flex_direction_t axis, float value) { float min = CSS_UNDEFINED; float max = CSS_UNDEFINED; @@ -672,32 +745,22 @@ static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) return boundValue; } -// When the user specifically sets a value for width or height -static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { - // The parent already computed us a width or height. We just skip it - if (isLayoutDimDefined(node, axis)) { - return; - } - // We only run if there's a width or height defined - if (!isStyleDimDefined(node, axis)) { - return; - } - - // The dimensions can never be smaller than the padding and border - node->layout.dimensions[dim[axis]] = fmaxf( - boundAxis(node, axis, node->style.dimensions[dim[axis]]), - getPaddingAndBorderAxis(node, axis) - ); +// Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the +// padding and border amount. +static float boundAxis(css_node_t* node, css_flex_direction_t axis, float value) { + return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis)); } -static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) { - child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] - - child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]]; - } +static void setTrailingPosition(css_node_t* node, css_node_t* child, css_flex_direction_t axis) { + float size = child->style.position_type == CSS_POSITION_ABSOLUTE ? + 0 : + child->layout.measured_dimensions[dim[axis]]; + child->layout.position[trailing[axis]] = node->layout.measured_dimensions[dim[axis]] - size - child->layout.position[pos[axis]]; +} // If both left and right are defined, then use left. Otherwise return // +left or -right depending on which is defined. -static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { +static float getRelativePosition(css_node_t* node, css_flex_direction_t axis) { float lead = node->style.position[leading[axis]]; if (!isUndefined(lead)) { return lead; @@ -705,352 +768,388 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { return -getPosition(node, trailing[axis]); } -static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) { - /** START_GENERATED **/ - css_direction_t direction = resolveDirection(node, parentDirection); +static void setPosition(css_node_t* node, css_direction_t direction) { css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); - css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); + + node->layout.position[leading[mainAxis]] = getLeadingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[leading[crossAxis]] = getLeadingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + node->layout.position[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); +} - // Handle width and height style attributes - setDimensionFromStyle(node, mainAxis); - setDimensionFromStyle(node, crossAxis); +// +// This is the main routine that implements a subset of the flexbox layout algorithm +// described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/. +// +// Limitations of this algorithm, compared to the full standard: +// * Display property is always assumed to be 'flex' except for Text nodes, which +// are assumed to be 'inline-flex'. +// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are +// stacked in document order. +// * The 'order' property is not supported. The order of flex items is always defined +// by document order. +// * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse' +// and 'hidden' are not supported. +// * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The +// rarely-used 'wrap-reverse' is not supported. +// * Rather than allowing arbitrary combinations of flexGrow, flexShrink and +// flexBasis, this algorithm supports only the three most common combinations: +// flex: 0 is equiavlent to flex: 0 0 auto +// flex: n (where n is a positive value) is equivalent to flex: n 1 auto +// If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0 +// This is faster because the content doesn't need to be measured, but it's +// less flexible because the basis is always 0 and can't be overriden with +// the width/height attributes. +// flex: -1 (or any negative value) is equivalent to flex: 0 1 auto +// * Margins cannot be specified as 'auto'. They must be specified in terms of pixel +// values, and the default value is 0. +// * The 'baseline' value is not supported for alignItems and alignSelf properties. +// * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be +// specified as pixel values, not as percentages. +// * There is no support for calculation of dimensions based on intrinsic aspect ratios +// (e.g. images). +// * There is no support for forced breaks. +// * It does not support vertical inline directions (top-to-bottom or bottom-to-top text). +// +// Deviations from standard: +// * Section 4.5 of the spec indicates that all flex items have a default minimum +// main size. For text blocks, for example, this is the width of the widest word. +// Calculating the minimum width is expensive, so we forego it and assume a default +// minimum main size of 0. +// * Min/Max sizes in the main axis are not honored when resolving flexible lengths. +// * The spec indicates that the default value for 'flexDirection' is 'row', but +// the algorithm below assumes a default of 'column'. +// +// Input parameters: +// - node: current node to be sized and layed out +// - availableWidth & availableHeight: available size to be used for sizing the node +// or CSS_UNDEFINED if the size is not available; interpretation depends on layout +// flags +// - parentDirection: the inline (text) direction within the parent (left-to-right or +// right-to-left) +// - widthMeasureMode: indicates the sizing rules for the width (see below for explanation) +// - heightMeasureMode: indicates the sizing rules for the height (see below for explanation) +// - performLayout: specifies whether the caller is interested in just the dimensions +// of the node or it requires the entire node and its subtree to be layed out +// (with final positions) +// +// Details: +// This routine is called recursively to lay out subtrees of flexbox elements. It uses the +// information in node.style, which is treated as a read-only input. It is responsible for +// setting the layout.direction and layout.measured_dimensions fields for the input node as well +// as the layout.position and layout.line_index fields for its child nodes. The +// layout.measured_dimensions field includes any border or padding for the node but does +// not include margins. +// +// The spec describes four different layout modes: "fill available", "max content", "min content", +// and "fit content". Of these, we don't use "min content" because we don't support default +// minimum main sizes (see above for details). Each of our measure modes maps to a layout mode +// from the spec (https://www.w3.org/TR/css3-sizing/#terms): +// - CSS_MEASURE_MODE_UNDEFINED: max content +// - CSS_MEASURE_MODE_EXACTLY: fill available +// - CSS_MEASURE_MODE_AT_MOST: fit content +// +// When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of +// undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension. +// +static void layoutNodeImpl(css_node_t* node, float availableWidth, float availableHeight, + css_direction_t parentDirection, css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, bool performLayout) { + /** START_GENERATED **/ - // Set the resolved resolution in the node's layout + assert(isUndefined(availableWidth) ? widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED : true); // availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED + assert(isUndefined(availableHeight) ? heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED : true); // availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED + + float paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); + float marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + float marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + + // Set the resolved resolution in the node's layout. + css_direction_t direction = resolveDirection(node, parentDirection); node->layout.direction = direction; - // The position is set by the parent, but we need to complete it with a - // delta composed of the margin and left/top/right/bottom - node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - - // Inline immutable values from the target node to avoid excessive method - // invocations during the layout calculation. - int childCount = node->children_count; - float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis); - float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - + // For content (text) nodes, determine the dimensions based on the text contents. if (isMeasureDefined(node)) { - bool isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis); + float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + + if (widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && heightMeasureMode == CSS_MEASURE_MODE_EXACTLY) { - float width = CSS_UNDEFINED; - css_measure_mode_t widthMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, resolvedRowAxis)) { - width = node->style.dimensions[CSS_WIDTH]; - widthMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isResolvedRowDimDefined) { - width = node->layout.dimensions[dim[resolvedRowAxis]]; - widthMode = CSS_MEASURE_MODE_EXACTLY; + // Don't bother sizing the text if both dimensions are already defined. + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); + } else if (innerWidth <= 0) { + + // Don't bother sizing the text if there's no horizontal space. + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { - width = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis); - widthMode = CSS_MEASURE_MODE_AT_MOST; - } - width -= paddingAndBorderAxisResolvedRow; - if (isUndefined(width)) { - widthMode = CSS_MEASURE_MODE_UNDEFINED; - } - float height = CSS_UNDEFINED; - css_measure_mode_t heightMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node->style.dimensions[CSS_HEIGHT]; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else { - height = parentMaxHeight - - getMarginAxis(node, resolvedRowAxis); - heightMode = CSS_MEASURE_MODE_AT_MOST; - } - height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - if (isUndefined(height)) { - heightMode = CSS_MEASURE_MODE_UNDEFINED; - } - - // We only need to give a dimension for the text if we haven't got any - // for it computed yet. It can either be from the style attribute or because - // the element is flexible. - bool isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined; - bool isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && - isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); - - // Let's not measure the text if we already know both dimensions - if (isRowUndefined || isColumnUndefined) { + // Measure the text under the current constraints. css_dim_t measureDim = node->measure( node->context, - width, - widthMode, - height, - heightMode + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode ); - if (isRowUndefined) { - node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + - paddingAndBorderAxisResolvedRow; - } - if (isColumnUndefined) { - node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + - paddingAndBorderAxisColumn; - } + + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + measureDim.dimensions[CSS_WIDTH] + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + measureDim.dimensions[CSS_HEIGHT] + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); } - if (childCount == 0) { + + return; + } + + // For nodes with no children, use the available values if they were provided, or + // the minimum size as indicated by the padding and border sizes. + int childCount = node->children_count; + if (childCount == 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); + return; + } + + // If we're not being asked to perform a full layout, we can handle a number of common + // cases here without incurring the cost of the remaining function. + if (!performLayout) { + // If we're being asked to size the content with an at most constraint but there is no available width, + // the measurement will always be zero. + if (widthMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 && + heightMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + if (widthMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn)); + return; + } + + if (heightMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow)); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + // If we're being asked to use an exact width/height, there's no need to measure the children. + if (widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && heightMeasureMode == CSS_MEASURE_MODE_EXACTLY) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); return; } } - bool isNodeFlexWrap = isFlexWrap(node); - + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); + css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); + bool isMainAxisRow = isRowDirection(mainAxis); css_justify_t justifyContent = node->style.justify_content; - - float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); - float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); - float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); - float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); - - bool isMainDimDefined = isLayoutDimDefined(node, mainAxis); - bool isCrossDimDefined = isLayoutDimDefined(node, crossAxis); - bool isMainRowDirection = isRowDirection(mainAxis); - - int i; - int ii; - css_node_t* child; - css_flex_direction_t axis; + bool isNodeFlexWrap = isFlexWrap(node); css_node_t* firstAbsoluteChild = NULL; css_node_t* currentAbsoluteChild = NULL; - float definedMainDim = CSS_UNDEFINED; - if (isMainDimDefined) { - definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; + float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); + float trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis); + float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); + float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); + float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); + + css_measure_mode_t measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + css_measure_mode_t measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; + float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + css_node_t* child; + int i; + float childWidth; + float childHeight; + css_measure_mode_t childWidthMeasureMode; + css_measure_mode_t childHeightMeasureMode; + for (i = 0; i < childCount; i++) { + child = node->get_child(node->context, i); + + if (performLayout) { + // Set the initial position (relative to the parent). + css_direction_t childDirection = resolveDirection(child, direction); + setPosition(child, childDirection); + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if (child->style.position_type == CSS_POSITION_ABSOLUTE) { + + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild == NULL) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild != NULL) { + currentAbsoluteChild->next_child = child; + } + currentAbsoluteChild = child; + child->next_child = NULL; + } else { + + if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + + // The width is definite, so use that as the flex basis. + child->layout.flex_basis = fmaxf(child->style.dimensions[CSS_WIDTH], getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW)); + } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + + // The height is definite, so use that as the flex basis. + child->layout.flex_basis = fmaxf(child->style.dimensions[CSS_HEIGHT], getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN)); + } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) { + + // If the basis isn't 'auto', it is assumed to be zero. + child->layout.flex_basis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis)); + } else { + + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + childWidth = child->style.dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = child->style.dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + // Measure the child + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "measure"); + + child->layout.flex_basis = fmaxf(isMainAxisRow ? child->layout.measured_dimensions[CSS_WIDTH] : child->layout.measured_dimensions[CSS_HEIGHT], getPaddingAndBorderAxis(child, mainAxis)); + } + } } - // We want to execute the next two loops one per line with flex-wrap - int startLine = 0; - int endLine = 0; - // int nextOffset = 0; - int alreadyComputedNextLayout = 0; - // We aggregate the total dimensions of the container in those two variables - float linesCrossDim = 0; - float linesMainDim = 0; - int linesCount = 0; - while (endLine < childCount) { - // Layout non flexible children and count children by type + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + int startOfLineIndex = 0; + int endOfLineIndex = 0; + + // Number of lines. + int lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; - // mainContentDim is accumulation of the dimensions and margin of all the - // non flexible children. This will be used in order to either set the - // dimensions of the node if none already exist, or to compute the - // remaining space left for the flexible children. - float mainContentDim = 0; + // Max main dimension of all the lines. + float maxLineMainDim = 0; - // There are three kind of children, non flexible, flexible and absolute. - // We need to know how many there are in order to distribute the space. - int flexibleChildrenCount = 0; - float totalFlexible = 0; - int nonFlexibleChildrenCount = 0; + while (endOfLineIndex < childCount) { + + // Number of items on the currently line. May be different than the difference + // between start and end indicates because we skip over absolute-positioned items. + int itemsOnLine = 0; - // Use the line loop to position children in the main axis for as long - // as they are using a simple stacking behaviour. Children that are - // immediately stacked in the initial loop will not be touched again - // in . - bool isSimpleStackMain = - (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) || - (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER); - int firstComplexMain = (isSimpleStackMain ? childCount : startLine); + // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin + // of all the children on the current line. This will be used in order to + // either set the dimensions of the node if none already exist or to compute + // the remaining space left for the flexible children. + float sizeConsumedOnCurrentLine = 0; - // Use the initial line loop to position children in the cross axis for - // as long as they are relatively positioned with alignment STRETCH or - // FLEX_START. Children that are immediately stacked in the initial loop - // will not be touched again in . - bool isSimpleStackCross = true; - int firstComplexCross = childCount; + float totalFlexGrowFactors = 0; + float totalFlexShrinkScaledFactors = 0; - css_node_t* firstFlexChild = NULL; - css_node_t* currentFlexChild = NULL; + i = startOfLineIndex; - float mainDim = leadingPaddingAndBorderMain; - float crossDim = 0; + // Maintain a linked list of the child nodes that can shrink and/or grow. + css_node_t* firstRelativeChild = NULL; + css_node_t* currentRelativeChild = NULL; - float maxWidth = CSS_UNDEFINED; - float maxHeight = CSS_UNDEFINED; - for (i = startLine; i < childCount; ++i) { + // Add items to the current line until it's full or we run out of items. + while (i < childCount) { child = node->get_child(node->context, i); - child->line_index = linesCount; + child->line_index = lineCount; - child->next_absolute_child = NULL; - child->next_flex_child = NULL; - - css_align_t alignItem = getAlignItem(node, child); - - // Pre-fill cross axis dimensions when the child is using stretch before - // we call the recursive layout pass - if (alignItem == CSS_ALIGN_STRETCH && - child->style.position_type == CSS_POSITION_RELATIVE && - isCrossDimDefined && - !isStyleDimDefined(child, crossAxis)) { - child->layout.dimensions[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) { - // Store a private linked list of absolutely positioned children - // so that we can efficiently traverse them later. - if (firstAbsoluteChild == NULL) { - firstAbsoluteChild = child; + if (child->style.position_type != CSS_POSITION_ABSOLUTE) { + float outerFlexBasis = child->layout.flex_basis + getMarginAxis(child, mainAxis); + + // If this is a multi-line flow and this item pushes us over the available size, we've + // hit the end of the current line. Break out of the loop and lay out the current line. + if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) { + break; } - if (currentAbsoluteChild != NULL) { - currentAbsoluteChild->next_absolute_child = child; - } - currentAbsoluteChild = child; - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(child, axis) && - isPosDefined(child, leading[axis]) && - isPosDefined(child, trailing[axis])) { - child->layout.dimensions[dim[axis]] = fmaxf( - boundAxis(child, axis, node->layout.dimensions[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis])), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + sizeConsumedOnCurrentLine += outerFlexBasis; + itemsOnLine++; + + if (isFlex(child)) { + totalFlexGrowFactors += getFlexGrowFactor(child); + + // Unlike the grow factor, the shrink factor is scaled relative to the child + // dimension. + totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child->layout.flex_basis; } + + // Store a private linked list of children that need to be layed out. + if (firstRelativeChild == NULL) { + firstRelativeChild = child; + } + if (currentRelativeChild != NULL) { + currentRelativeChild->next_child = child; + } + currentRelativeChild = child; + child->next_child = NULL; } - - float nextContentDim = 0; - - // It only makes sense to consider a child flexible if we have a computed - // dimension for the node-> - if (isMainDimDefined && isFlex(child)) { - flexibleChildrenCount++; - totalFlexible += child->style.flex; - - // Store a private linked list of flexible children so that we can - // efficiently traverse them later. - if (firstFlexChild == NULL) { - firstFlexChild = child; - } - if (currentFlexChild != NULL) { - currentFlexChild->next_flex_child = child; - } - currentFlexChild = child; - - // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information, which represents - // the smallest possible size for the child, to compute the remaining - // available space. - nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + - getMarginAxis(child, mainAxis); - - } else { - maxWidth = CSS_UNDEFINED; - maxHeight = CSS_UNDEFINED; - - if (!isMainRowDirection) { - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - } else { - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - } - - // This is the main recursive call. We layout non flexible children. - if (alreadyComputedNextLayout == 0) { - layoutNode(child, maxWidth, maxHeight, direction); - } - - // Absolute positioned elements do not take part of the layout, so we - // don't use them to compute mainContentDim - if (child->style.position_type == CSS_POSITION_RELATIVE) { - nonFlexibleChildrenCount++; - // At this point we know the final size and margin of the element. - nextContentDim = getDimWithMargin(child, mainAxis); - } - } - - // The element we are about to add would make us go to the next line - if (isNodeFlexWrap && - isMainDimDefined && - mainContentDim + nextContentDim > definedMainDim && - // If there's only one element, then it's bigger than the content - // and needs its own line - i != startLine) { - nonFlexibleChildrenCount--; - alreadyComputedNextLayout = 1; - break; - } - - // Disable simple stacking in the main axis for the current line as - // we found a non-trivial child-> The remaining children will be laid out - // in . - if (isSimpleStackMain && - (child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) { - isSimpleStackMain = false; - firstComplexMain = i; - } - - // Disable simple stacking in the cross axis for the current line as - // we found a non-trivial child-> The remaining children will be laid out - // in . - if (isSimpleStackCross && - (child->style.position_type != CSS_POSITION_RELATIVE || - (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) || - (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) { - isSimpleStackCross = false; - firstComplexCross = i; - } - - if (isSimpleStackMain) { - child->layout.position[pos[mainAxis]] += mainDim; - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); - } - - mainDim += getDimWithMargin(child, mainAxis); - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); - } - - if (isSimpleStackCross) { - child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); - } - } - - alreadyComputedNextLayout = 0; - mainContentDim += nextContentDim; - endLine = i + 1; + + i++; + endOfLineIndex++; } - - // Layout flexible children and allocate empty space + + // If we don't need to measure the cross axis, we can skip the entire flex step. + bool canSkipFlex = !performLayout && measureModeCrossDim == CSS_MEASURE_MODE_EXACTLY; // In order to position the elements in the main axis, we have two // controls. The space between the beginning and the first element @@ -1058,212 +1157,300 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM float leadingMainDim = 0; float betweenMainDim = 0; - // The remaining available space that needs to be allocated - float remainingMainDim = 0; - if (isMainDimDefined) { - remainingMainDim = definedMainDim - mainContentDim; - } else { - remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + float remainingFreeSpace = 0; + if (!isUndefined(availableInnerMainDim)) { + remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; + } else if (sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized based on its content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine; + } + + float remainingFreeSpaceAfterFlex = remainingFreeSpace; + + if (!canSkipFlex) { + float childFlexBasis; + float flexShrinkScaledFactor; + float flexGrowFactor; + float baseMainSize; + float boundMainSize; + + // Do two passes over the flex items to figure out how to distribute the remaining space. + // The first pass finds the items whose min/max constraints trigger, freezes them at those + // sizes, and excludes those sizes from the remaining space. The second pass sets the size + // of each flexible item. It distributes the remaining space amongst the items whose min/max + // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing + // their min/max constraints to trigger again. + // + // This two pass approach for resolving min/max constraints deviates from the spec. The + // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process + // that needs to be repeated a variable number of times. The algorithm implemented here + // won't handle all cases but it was simpler to implement and it mitigates performance + // concerns because we know exactly how many passes it'll do. + + // First pass: detect the flex items whose min/max constraints trigger + float deltaFreeSpace = 0; + float deltaFlexShrinkScaledFactors = 0; + float deltaFlexGrowFactors = 0; + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != NULL) { + childFlexBasis = currentRelativeChild->layout.flex_basis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; + } + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexGrowFactors -= flexGrowFactor; + } + } + } + + currentRelativeChild = currentRelativeChild->next_child; + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; + totalFlexGrowFactors += deltaFlexGrowFactors; + remainingFreeSpace += deltaFreeSpace; + remainingFreeSpaceAfterFlex = remainingFreeSpace; + + // Second pass: resolve the sizes of the flexible items + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != NULL) { + childFlexBasis = currentRelativeChild->layout.flex_basis; + float updatedMainSize = childFlexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor); + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor); + } + } + + remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + + if (isMainAxisRow) { + childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childHeight = currentRelativeChild->style.dimensions[CSS_HEIGHT] + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } else { + childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childWidth = currentRelativeChild->style.dimensions[CSS_WIDTH] + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } + + bool requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) && + getAlignItem(node, currentRelativeChild) == CSS_ALIGN_STRETCH; + + // Recursively call the layout algorithm for this child with the updated main size. + layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, "flex"); + + currentRelativeChild = currentRelativeChild->next_child; + } + } + + remainingFreeSpace = remainingFreeSpaceAfterFlex; + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main axis. + // Their dimensions are also set in the cross axis with the exception of items + // that are aligned "stretch". We need to compute these stretch values and + // set the final positions. + + // If we are using "at most" rules in the main axis, we won't distribute + // any remaining space at this point. + if (measureModeMainDim == CSS_MEASURE_MODE_AT_MOST) { + remainingFreeSpace = 0; } - // If there are flexible children in the mix, they are going to fill the - // remaining space - if (flexibleChildrenCount != 0) { - float flexibleMainDim = remainingMainDim / totalFlexible; - float baseMainDim; - float boundMainDim; - - // If the flex share of remaining space doesn't meet min/max bounds, - // remove this child from flex calculations. - currentFlexChild = firstFlexChild; - while (currentFlexChild != NULL) { - baseMainDim = flexibleMainDim * currentFlexChild->style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis); - boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); - - if (baseMainDim != boundMainDim) { - remainingMainDim -= boundMainDim; - totalFlexible -= currentFlexChild->style.flex; - } - - currentFlexChild = currentFlexChild->next_flex_child; - } - flexibleMainDim = remainingMainDim / totalFlexible; - - // The non flexible children can overflow the container, in this case - // we should just assume that there is no space available. - if (flexibleMainDim < 0) { - flexibleMainDim = 0; - } - - currentFlexChild = firstFlexChild; - while (currentFlexChild != NULL) { - // At this point we know the final size of the element in the main - // dimension - currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, - flexibleMainDim * currentFlexChild->style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis) - ); - - maxWidth = CSS_UNDEFINED; - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - maxHeight = CSS_UNDEFINED; - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else if (isMainRowDirection) { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - - // And we recursively call the layout algorithm for this child - layoutNode(currentFlexChild, maxWidth, maxHeight, direction); - - child = currentFlexChild; - currentFlexChild = currentFlexChild->next_flex_child; - child->next_flex_child = NULL; - } - - // We use justifyContent to figure out how to allocate the remaining - // space available - } else if (justifyContent != CSS_JUSTIFY_FLEX_START) { + // Use justifyContent to figure out how to allocate the remaining space + // available in the main axis. + if (justifyContent != CSS_JUSTIFY_FLEX_START) { if (justifyContent == CSS_JUSTIFY_CENTER) { - leadingMainDim = remainingMainDim / 2; + leadingMainDim = remainingFreeSpace / 2; } else if (justifyContent == CSS_JUSTIFY_FLEX_END) { - leadingMainDim = remainingMainDim; + leadingMainDim = remainingFreeSpace; } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) { - remainingMainDim = fmaxf(remainingMainDim, 0); - if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) { - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + remainingFreeSpace = fmaxf(remainingFreeSpace, 0); + if (itemsOnLine > 1) { + betweenMainDim = remainingFreeSpace / (itemsOnLine - 1); } else { betweenMainDim = 0; } } else if (justifyContent == CSS_JUSTIFY_SPACE_AROUND) { // Space on the edges is half of the space between elements - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount); + betweenMainDim = remainingFreeSpace / itemsOnLine; leadingMainDim = betweenMainDim / 2; } } - // Position elements in the main axis and compute dimensions + float mainDim = leadingPaddingAndBorderMain + leadingMainDim; + float crossDim = 0; - // At this point, all the children have their dimensions set. We need to - // find their position. In order to do that, we accumulate data in - // variables that are also useful to compute the total dimensions of the - // container! - mainDim += leadingMainDim; - - for (i = firstComplexMain; i < endLine; ++i) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { child = node->get_child(node->context, i); if (child->style.position_type == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { - // In case the child is position absolute and has left/top being - // defined, we override the position to whatever the user said - // (and margin/border). - child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + - getLeadingBorder(node, mainAxis) + - getLeadingMargin(child, mainAxis); - } else { - // If the child is position absolute (without top/left) or relative, - // we put it at the current accumulated offset. - child->layout.position[pos[mainAxis]] += mainDim; - - // Define the trailing position accordingly. - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + + getLeadingBorder(node, mainAxis) + + getLeadingMargin(child, mainAxis); } - - // Now that we placed the element, we need to update the variables - // We only need to do that for relative elements. Absolute elements + } else { + if (performLayout) { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child->layout.position[pos[mainAxis]] += mainDim; + } + + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements // do not take part in that phase. if (child->style.position_type == CSS_POSITION_RELATIVE) { - // The main dimension is the sum of all the elements dimension plus - // the spacing. - mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); - // The cross dimension is the max of the elements dimension since there - // can only be one element in that cross dimension. - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims because + // they weren't computed. This means we can't call getDimWithMargin. + mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child->layout.flex_basis; + crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); + + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis)); + } } } } - float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; - if (!isCrossDimDefined) { - containerCrossAxis = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); + mainDim += trailingPaddingAndBorderMain; + + float containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim == CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim == CSS_MEASURE_MODE_AT_MOST) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; + + if (measureModeCrossDim == CSS_MEASURE_MODE_AT_MOST) { + containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim); + } } - // Position elements in the cross axis - for (i = firstComplexCross; i < endLine; ++i) { - child = node->get_child(node->context, i); + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim == CSS_MEASURE_MODE_EXACTLY) { + crossDim = availableInnerCrossDim; + } - if (child->style.position_type == CSS_POSITION_ABSOLUTE && - isPosDefined(child, leading[crossAxis])) { - // In case the child is absolutely positionned and has a - // top/left/bottom/right being set, we override all the previously - // computed positions to set it correctly. - child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + - getLeadingBorder(node, crossAxis) + - getLeadingMargin(child, crossAxis); + // Clamp to the min/max size specified on the container. + crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; - } else { - float leadingCrossDim = leadingPaddingAndBorderCross; + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { + child = node->get_child(node->context, i); - // For a relative children, we're either using alignItems (parent) or - // alignSelf (child) in order to determine the position in the cross axis - if (child->style.position_type == CSS_POSITION_RELATIVE) { - /*eslint-disable */ - // This variable is intentionally re-defined as the code is transpiled to a block scope language + if (child->style.position_type == CSS_POSITION_ABSOLUTE) { + // If the child is absolutely positioned and has a top/left/bottom/right + // set, override all the previously computed positions to set it correctly. + if (isPosDefined(child, leading[crossAxis])) { + child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + + getLeadingBorder(node, crossAxis) + + getLeadingMargin(child, crossAxis); + } else { + child->layout.position[pos[crossAxis]] = leadingPaddingAndBorderCross + + getLeadingMargin(child, crossAxis); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis css_align_t alignItem = getAlignItem(node, child); - /*eslint-enable */ + + // If the child uses align stretch, we need to lay it out one more time, this time + // forcing the cross-axis size to be the computed cross size for the current line. if (alignItem == CSS_ALIGN_STRETCH) { - // You can only stretch if the dimension has not already been defined - // previously. - if (!isStyleDimDefined(child, crossAxis)) { - float dimCrossAxis = child->layout.dimensions[dim[crossAxis]]; - child->layout.dimensions[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, containerCrossAxis - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - - // If the size has changed, and this child has children we need to re-layout this child - if (dimCrossAxis != child->layout.dimensions[dim[crossAxis]] && child->children_count > 0) { - // Reset child margins before re-layout as they are added back in layoutNode and would be doubled - child->layout.position[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child->layout.position[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child->layout.position[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - child->layout.position[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - - layoutNode(child, maxWidth, maxHeight, direction); - } + childWidth = child->layout.measured_dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childHeight = child->layout.measured_dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + bool isCrossSizeDefinite = false; + + if (isMainAxisRow) { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN); + childHeight = crossDim; + } else { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW); + childWidth = crossDim; + } + + // If the child defines a definite size for its cross axis, there's no need to stretch. + if (!isCrossSizeDefinite) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, "stretch"); } } else if (alignItem != CSS_ALIGN_FLEX_START) { - // The remaining space between the parent dimensions+padding and child - // dimensions+margin. - float remainingCrossDim = containerCrossAxis - - paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis); + float remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis); if (alignItem == CSS_ALIGN_CENTER) { leadingCrossDim += remainingCrossDim / 2; @@ -1271,41 +1458,25 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM leadingCrossDim += remainingCrossDim; } } - } - // And we apply the position - child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; - - // Define the trailing position accordingly. - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); + // And we apply the position + child->layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; } } } - linesCrossDim += crossDim; - linesMainDim = fmaxf(linesMainDim, mainDim); - linesCount += 1; - startLine = endLine; + totalLineCrossDim += crossDim; + maxLineMainDim = fmaxf(maxLineMainDim, mainDim); + + // Reset variables for new line. + lineCount++; + startOfLineIndex = endOfLineIndex; + endOfLineIndex = startOfLineIndex; } - // - // - // Note(prenaux): More than one line, we need to layout the crossAxis - // according to alignContent. - // - // Note that we could probably remove and handle the one line case - // here too, but for the moment this is safer since it won't interfere with - // previously working code. - // - // See specs: - // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm - // section 9.4 - // - if (linesCount > 1 && isCrossDimDefined) { - float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross; - float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) { + float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; float crossDimLead = 0; float currentLead = leadingPaddingAndBorderCross; @@ -1316,19 +1487,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM } else if (alignContent == CSS_ALIGN_CENTER) { currentLead += remainingAlignContentDim / 2; } else if (alignContent == CSS_ALIGN_STRETCH) { - if (nodeCrossAxisInnerSize > linesCrossDim) { - crossDimLead = (remainingAlignContentDim / linesCount); + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = (remainingAlignContentDim / lineCount); } } int endIndex = 0; - for (i = 0; i < linesCount; ++i) { + for (i = 0; i < lineCount; ++i) { int startIndex = endIndex; + int j; // compute the line's height and find the endIndex float lineHeight = 0; - for (ii = startIndex; ii < childCount; ++ii) { - child = node->get_child(node->context, ii); + for (j = startIndex; j < childCount; ++j) { + child = node->get_child(node->context, j); if (child->style.position_type != CSS_POSITION_RELATIVE) { continue; } @@ -1336,33 +1508,33 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM break; } if (isLayoutDimDefined(child, crossAxis)) { - lineHeight = fmaxf( - lineHeight, - child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis) - ); + lineHeight = fmaxf(lineHeight, + child->layout.measured_dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis)); } } - endIndex = ii; + endIndex = j; lineHeight += crossDimLead; - for (ii = startIndex; ii < endIndex; ++ii) { - child = node->get_child(node->context, ii); - if (child->style.position_type != CSS_POSITION_RELATIVE) { - continue; - } + if (performLayout) { + for (j = startIndex; j < endIndex; ++j) { + child = node->get_child(node->context, j); + if (child->style.position_type != CSS_POSITION_RELATIVE) { + continue; + } - css_align_t alignContentAlignItem = getAlignItem(node, child); - if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { - child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { - child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]]; - } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { - float childHeight = child->layout.dimensions[dim[crossAxis]]; - child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; - } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { - child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - // TODO(prenaux): Correctly set the height of items with undefined - // (auto) crossAxis dimension. + css_align_t alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { + child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.measured_dimensions[dim[crossAxis]]; + } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { + childHeight = child->layout.measured_dimensions[dim[crossAxis]]; + child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with indefinite + // (auto) crossAxis dimension. + } } } @@ -1370,139 +1542,345 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM } } - bool needsMainTrailingPos = false; - bool needsCrossTrailingPos = false; + // STEP 9: COMPUTING FINAL DIMENSIONS + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (!isMainDimDefined) { - node->layout.dimensions[dim[mainAxis]] = fmaxf( - // We're missing the last padding at this point to get the final - // dimension - boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), - // We can never assign a width smaller than the padding and borders - paddingAndBorderAxisMain - ); + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim == CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->layout.measured_dimensions[dim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim); + } else if (measureModeMainDim == CSS_MEASURE_MODE_AT_MOST) { + node->layout.measured_dimensions[dim[mainAxis]] = fmaxf( + fminf(availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)), + paddingAndBorderAxisMain); + } + + if (measureModeCrossDim == CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->layout.measured_dimensions[dim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross); + } else if (measureModeCrossDim == CSS_MEASURE_MODE_AT_MOST) { + node->layout.measured_dimensions[dim[crossAxis]] = fmaxf( + fminf(availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)), + paddingAndBorderAxisCross); + } + + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + if (performLayout) { + bool needsMainTrailingPos = false; + bool needsCrossTrailingPos = false; if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsMainTrailingPos = true; } - } - - if (!isCrossDimDefined) { - node->layout.dimensions[dim[crossAxis]] = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsCrossTrailingPos = true; } - } - // Set trailing position if necessary - if (needsMainTrailingPos || needsCrossTrailingPos) { - for (i = 0; i < childCount; ++i) { - child = node->get_child(node->context, i); + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node->get_child(node->context, i); - if (needsMainTrailingPos) { - setTrailingPosition(node, child, mainAxis); - } + if (needsMainTrailingPos) { + setTrailingPosition(node, child, mainAxis); + } - if (needsCrossTrailingPos) { - setTrailingPosition(node, child, crossAxis); + if (needsCrossTrailingPos) { + setTrailingPosition(node, child, crossAxis); + } } } } - - // Calculate dimensions for absolutely positioned elements + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN currentAbsoluteChild = firstAbsoluteChild; while (currentAbsoluteChild != NULL) { - // Pre-fill dimensions when using absolute position and both offsets for - // the axis are defined (either both left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + // Now that we know the bounds of the container, perform layout again on the + // absolutely-positioned children. + if (performLayout) { - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(currentAbsoluteChild, axis) && - isPosDefined(currentAbsoluteChild, leading[axis]) && - isPosDefined(currentAbsoluteChild, trailing[axis])) { - currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf( - boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] - - getBorderAxis(node, axis) - - getMarginAxis(currentAbsoluteChild, axis) - - getPosition(currentAbsoluteChild, leading[axis]) - - getPosition(currentAbsoluteChild, trailing[axis]) - ), - // You never want to go smaller than padding - getPaddingAndBorderAxis(currentAbsoluteChild, axis) - ); + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = currentAbsoluteChild->style.dimensions[CSS_WIDTH] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + } else { + // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) { + childWidth = node->layout.measured_dimensions[CSS_WIDTH] - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) - + (currentAbsoluteChild->style.position[CSS_LEFT] + currentAbsoluteChild->style.position[CSS_RIGHT]); + childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth); + } + } + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = currentAbsoluteChild->style.dimensions[CSS_HEIGHT] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } else { + // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) { + childHeight = node->layout.measured_dimensions[CSS_HEIGHT] - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) - + (currentAbsoluteChild->style.position[CSS_TOP] + currentAbsoluteChild->style.position[CSS_BOTTOM]); + childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight); + } } - if (isPosDefined(currentAbsoluteChild, trailing[axis]) && - !isPosDefined(currentAbsoluteChild, leading[axis])) { - currentAbsoluteChild->layout.position[leading[axis]] = - node->layout.dimensions[dim[axis]] - - currentAbsoluteChild->layout.dimensions[dim[axis]] - - getPosition(currentAbsoluteChild, trailing[axis]); + // If we're still missing one or the other dimension, measure the content. + if (isUndefined(childWidth) || isUndefined(childHeight)) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "abs-measure"); + childWidth = currentAbsoluteChild->layout.measured_dimensions[CSS_WIDTH] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + childHeight = currentAbsoluteChild->layout.measured_dimensions[CSS_HEIGHT] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, "abs-layout"); + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) { + currentAbsoluteChild->layout.position[leading[CSS_FLEX_DIRECTION_ROW]] = + node->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + currentAbsoluteChild->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]); + } + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) { + currentAbsoluteChild->layout.position[leading[CSS_FLEX_DIRECTION_COLUMN]] = + node->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + currentAbsoluteChild->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]); } } - child = currentAbsoluteChild; - currentAbsoluteChild = currentAbsoluteChild->next_absolute_child; - child->next_absolute_child = NULL; + currentAbsoluteChild = currentAbsoluteChild->next_child; } /** END_GENERATED **/ } -void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) { - css_layout_t *layout = &node->layout; - css_direction_t direction = node->style.direction; - layout->should_update = true; +int gDepth = 0; +bool gPrintTree = false; +bool gPrintChanges = false; +bool gPrintSkips = false; - bool skipLayout = - !node->is_dirty(node->context) && - eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) && - eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) && - eq(layout->last_parent_max_width, parentMaxWidth) && - eq(layout->last_parent_max_height, parentMaxHeight) && - eq(layout->last_direction, direction); +static const char* spacer = " "; - if (skipLayout) { - layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH]; - layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT]; - layout->position[CSS_TOP] = layout->last_position[CSS_TOP]; - layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT]; +static const char* getSpacer(unsigned long level) { + unsigned long spacerLen = strlen(spacer); + if (level > spacerLen) { + level = spacerLen; + } + return &spacer[spacerLen - level]; +} + +static const char* getModeName(css_measure_mode_t mode, bool performLayout) { + const char* kMeasureModeNames[CSS_MEASURE_MODE_COUNT] = { + "UNDEFINED", + "EXACTLY", + "AT_MOST" + }; + const char* kLayoutModeNames[CSS_MEASURE_MODE_COUNT] = { + "LAY_UNDEFINED", + "LAY_EXACTLY", + "LAY_AT_MOST" + }; + + if (mode >= CSS_MEASURE_MODE_COUNT) { + return ""; + } + + return performLayout? kLayoutModeNames[mode] : kMeasureModeNames[mode]; +} + + +// +// This is a wrapper around the layoutNodeImpl function. It determines +// whether the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as layoutNodeImpl (see above) +// Return parameter is true if layout was performed, false if skipped +// +bool layoutNodeInternal(css_node_t* node, float availableWidth, float availableHeight, + css_direction_t parentDirection, css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, bool performLayout, char* reason) { + css_layout_t* layout = &node->layout; + + gDepth++; + + bool needToVisitNode = (node->is_dirty(node->context) && layout->generation_count != gCurrentGenerationCount) || + layout->last_parent_direction != parentDirection; + + if (needToVisitNode) { + // Invalidate the cached results. + layout->next_cached_measurements_index = 0; + layout->cached_layout.width_measure_mode = (css_measure_mode_t)-1; + layout->cached_layout.height_measure_mode = (css_measure_mode_t)-1; + } + + css_cached_measurement_t* cachedResults = NULL; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the positions + // and dimensions for nodes in the subtree. The algorithm assumes that each node + // gets layed out a maximum of one time per tree layout, but multiple measurements + // may be required to resolve all of the flex dimensions. + if (performLayout) { + if (eq(layout->cached_layout.available_width, availableWidth) && + eq(layout->cached_layout.available_height, availableHeight) && + layout->cached_layout.width_measure_mode == widthMeasureMode && + layout->cached_layout.height_measure_mode == heightMeasureMode) { + + cachedResults = &layout->cached_layout; + } } else { - layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; - layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; - layout->last_parent_max_width = parentMaxWidth; - layout->last_parent_max_height = parentMaxHeight; - layout->last_direction = direction; + for (int i = 0; i < layout->next_cached_measurements_index; i++) { + if (eq(layout->cached_measurements[i].available_width, availableWidth) && + eq(layout->cached_measurements[i].available_height, availableHeight) && + layout->cached_measurements[i].width_measure_mode == widthMeasureMode && + layout->cached_measurements[i].height_measure_mode == heightMeasureMode) { - for (int i = 0, childCount = node->children_count; i < childCount; i++) { - resetNodeLayout(node->get_child(node->context, i)); + cachedResults = &layout->cached_measurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != NULL) { + layout->measured_dimensions[CSS_WIDTH] = cachedResults->computed_width; + layout->measured_dimensions[CSS_HEIGHT] = cachedResults->computed_height; + + if (gPrintChanges && gPrintSkips) { + printf("%s%d.{[skipped] ", getSpacer(gDepth), gDepth); + if (node->print) { + node->print(node->context); + } + printf("wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n", + getModeName(widthMeasureMode, performLayout), + getModeName(heightMeasureMode, performLayout), + availableWidth, availableHeight, + cachedResults->computed_width, cachedResults->computed_height, reason); + } + } else { + + if (gPrintChanges) { + printf("%s%d.{%s", getSpacer(gDepth), gDepth, needToVisitNode ? "*" : ""); + if (node->print) { + node->print(node->context); + } + printf("wm: %s, hm: %s, aw: %f ah: %f %s\n", + getModeName(widthMeasureMode, performLayout), + getModeName(heightMeasureMode, performLayout), + availableWidth, availableHeight, reason); } - layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection); + layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout); + + if (gPrintChanges) { + printf("%s%d.}%s", getSpacer(gDepth), gDepth, needToVisitNode ? "*" : ""); + if (node->print) { + node->print(node->context); + } + printf("wm: %s, hm: %s, d: (%f, %f) %s\n", + getModeName(widthMeasureMode, performLayout), + getModeName(heightMeasureMode, performLayout), + layout->measured_dimensions[CSS_WIDTH], layout->measured_dimensions[CSS_HEIGHT], reason); + } - layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; - layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; - layout->last_position[CSS_TOP] = layout->position[CSS_TOP]; - layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT]; + layout->last_parent_direction = parentDirection; + + if (cachedResults == NULL) { + if (layout->next_cached_measurements_index == CSS_MAX_CACHED_RESULT_COUNT) { + if (gPrintChanges) { + printf("Out of cache entries!\n"); + } + layout->next_cached_measurements_index = 0; + } + + css_cached_measurement_t* newCacheEntry; + if (performLayout) { + // Use the single layout cache entry. + newCacheEntry = &layout->cached_layout; + } else { + // Allocate a new measurement cache entry. + newCacheEntry = &layout->cached_measurements[layout->next_cached_measurements_index]; + layout->next_cached_measurements_index++; + } + + newCacheEntry->available_width = availableWidth; + newCacheEntry->available_height = availableHeight; + newCacheEntry->width_measure_mode = widthMeasureMode; + newCacheEntry->height_measure_mode = heightMeasureMode; + newCacheEntry->computed_width = layout->measured_dimensions[CSS_WIDTH]; + newCacheEntry->computed_height = layout->measured_dimensions[CSS_HEIGHT]; + } + } + + if (performLayout) { + node->layout.dimensions[CSS_WIDTH] = node->layout.measured_dimensions[CSS_WIDTH]; + node->layout.dimensions[CSS_HEIGHT] = node->layout.measured_dimensions[CSS_HEIGHT]; + layout->should_update = true; + } + + gDepth--; + layout->generation_count = gCurrentGenerationCount; + return (needToVisitNode || cachedResults == NULL); +} + +void layoutNode(css_node_t* node, float availableWidth, float availableHeight, css_direction_t parentDirection) { + // Increment the generation count. This will force the recursive routine to visit + // all dirty nodes at least once. Subsequent visits will be skipped if the input + // parameters don't change. + gCurrentGenerationCount++; + + // If the caller didn't specify a height/width, use the dimensions + // specified in the style. + if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + availableWidth = node->style.dimensions[CSS_WIDTH] + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + } + if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + availableHeight = node->style.dimensions[CSS_HEIGHT] + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + } + + css_measure_mode_t widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + css_measure_mode_t heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, "initial")) { + + setPosition(node, node->layout.direction); + + if (gPrintTree) { + print_css_node(node, CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN | CSS_PRINT_STYLE); + } } } -void resetNodeLayout(css_node_t *node) { - node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; - node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; - node->layout.position[CSS_LEFT] = 0; - node->layout.position[CSS_TOP] = 0; -} - #endif // CSS_LAYOUT_IMPLEMENTATION \ No newline at end of file diff --git a/dist/css-layout.jar b/dist/css-layout.jar index 5ebc995f4c504d4a69a90834868633c4843800ad..21cef567a1cd090fc626efb5b69189cfdcc76968 100644 GIT binary patch delta 14382 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 0 implies a basis of zero. + return getFlex(node) <= 0; + } + } + + function getFlexGrowFactor(node) { + // Flex grow is implied by positive values for flex. + if (getFlex(node) > 0) { + return getFlex(node); + } + return 0; + } + + function getFlexShrinkFactor(node) { + if (POSITIVE_FLEX_IS_AUTO) { + // A flex shrink factor of 1 is implied by non-zero values for flex. + if (getFlex(node) !== 0) { + return 1; + } + } else { + // A flex shrink factor of 1 is implied by negative values for flex. + if (getFlex(node) < 0) { + return 1; + } + } + return 0; + } function getLeadingMargin(node, axis) { if (node.style.marginStart !== undefined && isRowDirection(axis)) { @@ -283,10 +341,6 @@ var computeLayout = (function() { return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); } - function getBorderAxis(node, axis) { - return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); - } - function getMarginAxis(node, axis) { return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } @@ -366,13 +420,20 @@ var computeLayout = (function() { if (node.style.position) { return node.style.position; } - return 'relative'; + return CSS_POSITION_RELATIVE; + } + + function getOverflow(node) { + if (node.style.overflow) { + return node.style.overflow; + } + return CSS_OVERFLOW_VISIBLE; } function isFlex(node) { return ( getPositionType(node) === CSS_POSITION_RELATIVE && - node.style.flex > 0 + node.style.flex !== undefined && node.style.flex !== 0 ); } @@ -381,15 +442,15 @@ var computeLayout = (function() { } function getDimWithMargin(node, axis) { - return node.layout[dim[axis]] + getMarginAxis(node, axis); + return node.layout[measuredDim[axis]] + getMarginAxis(node, axis); } - - function isStyleDimDefined(node, axis) { + + function isStyleDimDefined(node, axis) { return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0; } - - function isLayoutDimDefined(node, axis) { - return node.layout[dim[axis]] !== undefined && node.layout[dim[axis]] >= 0; + + function isLayoutDimDefined(node, axis) { + return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0; } function isPosDefined(node, pos) { @@ -406,8 +467,8 @@ var computeLayout = (function() { } return 0; } - - function boundAxis(node, axis, value) { + + function boundAxisWithinMinAndMax(node, axis, value) { var min = { 'row': node.style.minWidth, 'row-reverse': node.style.minWidth, @@ -431,6 +492,13 @@ var computeLayout = (function() { } return boundValue; } + + function fminf(a, b) { + if (a < b) { + return a; + } + return b; + } function fmaxf(a, b) { if (a > b) { @@ -438,28 +506,18 @@ var computeLayout = (function() { } return b; } - - // When the user specifically sets a value for width or height - function setDimensionFromStyle(node, axis) { - // The parent already computed us a width or height. We just skip it - if (isLayoutDimDefined(node, axis)) { - return; - } - // We only run if there's a width or height defined - if (!isStyleDimDefined(node, axis)) { - return; - } - - // The dimensions can never be smaller than the padding and border - node.layout[dim[axis]] = fmaxf( - boundAxis(node, axis, node.style[dim[axis]]), - getPaddingAndBorderAxis(node, axis) - ); + + // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the + // padding and border amount. + function boundAxis(node, axis, value) { + return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis)); } function setTrailingPosition(node, child, axis) { - child.layout[trailing[axis]] = node.layout[dim[axis]] - - child.layout[dim[axis]] - child.layout[pos[axis]]; + var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ? + 0 : + child.layout[measuredDim[axis]]; + child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]]; } // If both left and right are defined, then use left. Otherwise return @@ -470,352 +528,392 @@ var computeLayout = (function() { } return -getPosition(node, trailing[axis]); } + + function setPosition(node, direction) { + var mainAxis = resolveAxis(getFlexDirection(node), direction); + var crossAxis = getCrossFlexDirection(mainAxis, direction); + + node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + } + + function assert(condition, message) { + if (!condition) { + throw new Error(message); + } + } + + // + // This is the main routine that implements a subset of the flexbox layout algorithm + // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/. + // + // Limitations of this algorithm, compared to the full standard: + // * Display property is always assumed to be 'flex' except for Text nodes, which + // are assumed to be 'inline-flex'. + // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are + // stacked in document order. + // * The 'order' property is not supported. The order of flex items is always defined + // by document order. + // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse' + // and 'hidden' are not supported. + // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The + // rarely-used 'wrap-reverse' is not supported. + // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and + // flexBasis, this algorithm supports only the three most common combinations: + // flex: 0 is equiavlent to flex: 0 0 auto + // flex: n (where n is a positive value) is equivalent to flex: n 1 auto + // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0 + // This is faster because the content doesn't need to be measured, but it's + // less flexible because the basis is always 0 and can't be overriden with + // the width/height attributes. + // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto + // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel + // values, and the default value is 0. + // * The 'baseline' value is not supported for alignItems and alignSelf properties. + // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be + // specified as pixel values, not as percentages. + // * There is no support for calculation of dimensions based on intrinsic aspect ratios + // (e.g. images). + // * There is no support for forced breaks. + // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text). + // + // Deviations from standard: + // * Section 4.5 of the spec indicates that all flex items have a default minimum + // main size. For text blocks, for example, this is the width of the widest word. + // Calculating the minimum width is expensive, so we forego it and assume a default + // minimum main size of 0. + // * Min/Max sizes in the main axis are not honored when resolving flexible lengths. + // * The spec indicates that the default value for 'flexDirection' is 'row', but + // the algorithm below assumes a default of 'column'. + // + // Input parameters: + // - node: current node to be sized and layed out + // - availableWidth & availableHeight: available size to be used for sizing the node + // or CSS_UNDEFINED if the size is not available; interpretation depends on layout + // flags + // - parentDirection: the inline (text) direction within the parent (left-to-right or + // right-to-left) + // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation) + // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation) + // - performLayout: specifies whether the caller is interested in just the dimensions + // of the node or it requires the entire node and its subtree to be layed out + // (with final positions) + // + // Details: + // This routine is called recursively to lay out subtrees of flexbox elements. It uses the + // information in node.style, which is treated as a read-only input. It is responsible for + // setting the layout.direction and layout.measured_dimensions fields for the input node as well + // as the layout.position and layout.line_index fields for its child nodes. The + // layout.measured_dimensions field includes any border or padding for the node but does + // not include margins. + // + // The spec describes four different layout modes: "fill available", "max content", "min content", + // and "fit content". Of these, we don't use "min content" because we don't support default + // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode + // from the spec (https://www.w3.org/TR/css3-sizing/#terms): + // - CSS_MEASURE_MODE_UNDEFINED: max content + // - CSS_MEASURE_MODE_EXACTLY: fill available + // - CSS_MEASURE_MODE_AT_MOST: fit content + // + // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of + // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension. + // + function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) { + assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED'); + assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED'); + + var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); + var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); - function layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, /*css_direction_t*/parentDirection) { + // Set the resolved resolution in the node's layout. var/*css_direction_t*/ direction = resolveDirection(node, parentDirection); - var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction); - var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction); - var/*(c)!css_flex_direction_t*//*(java)!int*/ resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); - - // Handle width and height style attributes - setDimensionFromStyle(node, mainAxis); - setDimensionFromStyle(node, crossAxis); - - // Set the resolved resolution in the node's layout node.layout.direction = direction; - // The position is set by the parent, but we need to complete it with a - // delta composed of the margin and left/top/right/bottom - node.layout[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node.layout[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node.layout[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - node.layout[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - - // Inline immutable values from the target node to avoid excessive method - // invocations during the layout calculation. - var/*int*/ childCount = node.children.length; - var/*float*/ paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis); - var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - + // For content (text) nodes, determine the dimensions based on the text contents. if (isMeasureDefined(node)) { - var/*bool*/ isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis); + var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + + if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) { - var/*float*/ width = CSS_UNDEFINED; - var/*css_measure_mode_t*/ widthMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, resolvedRowAxis)) { - width = node.style.width; - widthMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isResolvedRowDimDefined) { - width = node.layout[dim[resolvedRowAxis]]; - widthMode = CSS_MEASURE_MODE_EXACTLY; + // Don't bother sizing the text if both dimensions are already defined. + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); + } else if (innerWidth <= 0) { + + // Don't bother sizing the text if there's no horizontal space. + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { - width = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis); - widthMode = CSS_MEASURE_MODE_AT_MOST; - } - width -= paddingAndBorderAxisResolvedRow; - if (isUndefined(width)) { - widthMode = CSS_MEASURE_MODE_UNDEFINED; - } - var/*float*/ height = CSS_UNDEFINED; - var/*css_measure_mode_t*/ heightMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node.style.height; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]]; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else { - height = parentMaxHeight - - getMarginAxis(node, resolvedRowAxis); - heightMode = CSS_MEASURE_MODE_AT_MOST; - } - height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - if (isUndefined(height)) { - heightMode = CSS_MEASURE_MODE_UNDEFINED; - } - - // We only need to give a dimension for the text if we haven't got any - // for it computed yet. It can either be from the style attribute or because - // the element is flexible. - var/*bool*/ isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined; - var/*bool*/ isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && - isUndefined(node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]]); - - // Let's not measure the text if we already know both dimensions - if (isRowUndefined || isColumnUndefined) { + // Measure the text under the current constraints. var/*css_dim_t*/ measureDim = node.style.measure( /*(c)!node->context,*/ /*(java)!layoutContext.measureOutput,*/ - width, - widthMode, - height, - heightMode + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode ); - if (isRowUndefined) { - node.layout.width = measureDim.width + - paddingAndBorderAxisResolvedRow; - } - if (isColumnUndefined) { - node.layout.height = measureDim.height + - paddingAndBorderAxisColumn; - } + + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + measureDim.width + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + measureDim.height + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); } - if (childCount === 0) { + + return; + } + + // For nodes with no children, use the available values if they were provided, or + // the minimum size as indicated by the padding and border sizes. + var/*int*/ childCount = node.children.length; + if (childCount === 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); + return; + } + + // If we're not being asked to perform a full layout, we can handle a number of common + // cases here without incurring the cost of the remaining function. + if (!performLayout) { + // If we're being asked to size the content with an at most constraint but there is no available width, + // the measurement will always be zero. + if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 && + heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn)); + return; + } + + if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow)); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + // If we're being asked to use an exact width/height, there's no need to measure the children. + if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); return; } } + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction); + var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction); + var/*bool*/ isMainAxisRow = isRowDirection(mainAxis); + var/*css_justify_t*/ justifyContent = getJustifyContent(node); var/*bool*/ isNodeFlexWrap = isFlexWrap(node); - var/*css_justify_t*/ justifyContent = getJustifyContent(node); + var/*css_node_t**/ firstAbsoluteChild = undefined; + var/*css_node_t**/ currentAbsoluteChild = undefined; var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); + var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis); var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); + + var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; - var/*bool*/ isMainDimDefined = isLayoutDimDefined(node, mainAxis); - var/*bool*/ isCrossDimDefined = isLayoutDimDefined(node, crossAxis); - var/*bool*/ isMainRowDirection = isRowDirection(mainAxis); + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; + var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; - var/*int*/ i; - var/*int*/ ii; + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM var/*css_node_t**/ child; - var/*(c)!css_flex_direction_t*//*(java)!int*/ axis; + var/*int*/ i; + var/*float*/ childWidth; + var/*float*/ childHeight; + var/*css_measure_mode_t*/ childWidthMeasureMode; + var/*css_measure_mode_t*/ childHeightMeasureMode; + for (i = 0; i < childCount; i++) { + child = node.children[i]; - var/*css_node_t**/ firstAbsoluteChild = null; - var/*css_node_t**/ currentAbsoluteChild = null; + if (performLayout) { + // Set the initial position (relative to the parent). + var/*css_direction_t*/ childDirection = resolveDirection(child, direction); + setPosition(child, childDirection); + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if (getPositionType(child) === CSS_POSITION_ABSOLUTE) { - var/*float*/ definedMainDim = CSS_UNDEFINED; - if (isMainDimDefined) { - definedMainDim = node.layout[dim[mainAxis]] - paddingAndBorderAxisMain; + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild === undefined) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild !== undefined) { + currentAbsoluteChild.nextChild = child; + } + currentAbsoluteChild = child; + child.nextChild = undefined; + } else { + + if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + + // The width is definite, so use that as the flex basis. + child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW)); + } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + + // The height is definite, so use that as the flex basis. + child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN)); + } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) { + + // If the basis isn't 'auto', it is assumed to be zero. + child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis)); + } else { + + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + // Measure the child + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure'); + + child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis)); + } + } } - // We want to execute the next two loops one per line with flex-wrap - var/*int*/ startLine = 0; - var/*int*/ endLine = 0; - // var/*int*/ nextOffset = 0; - var/*int*/ alreadyComputedNextLayout = 0; - // We aggregate the total dimensions of the container in those two variables - var/*float*/ linesCrossDim = 0; - var/*float*/ linesMainDim = 0; - var/*int*/ linesCount = 0; - while (endLine < childCount) { - // Layout non flexible children and count children by type + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + var/*int*/ startOfLineIndex = 0; + var/*int*/ endOfLineIndex = 0; + + // Number of lines. + var/*int*/ lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + var/*float*/ totalLineCrossDim = 0; - // mainContentDim is accumulation of the dimensions and margin of all the - // non flexible children. This will be used in order to either set the - // dimensions of the node if none already exist, or to compute the - // remaining space left for the flexible children. - var/*float*/ mainContentDim = 0; + // Max main dimension of all the lines. + var/*float*/ maxLineMainDim = 0; - // There are three kind of children, non flexible, flexible and absolute. - // We need to know how many there are in order to distribute the space. - var/*int*/ flexibleChildrenCount = 0; - var/*float*/ totalFlexible = 0; - var/*int*/ nonFlexibleChildrenCount = 0; + while (endOfLineIndex < childCount) { + + // Number of items on the currently line. May be different than the difference + // between start and end indicates because we skip over absolute-positioned items. + var/*int*/ itemsOnLine = 0; - // Use the line loop to position children in the main axis for as long - // as they are using a simple stacking behaviour. Children that are - // immediately stacked in the initial loop will not be touched again - // in . - var/*bool*/ isSimpleStackMain = - (isMainDimDefined && justifyContent === CSS_JUSTIFY_FLEX_START) || - (!isMainDimDefined && justifyContent !== CSS_JUSTIFY_CENTER); - var/*int*/ firstComplexMain = (isSimpleStackMain ? childCount : startLine); + // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin + // of all the children on the current line. This will be used in order to + // either set the dimensions of the node if none already exist or to compute + // the remaining space left for the flexible children. + var/*float*/ sizeConsumedOnCurrentLine = 0; - // Use the initial line loop to position children in the cross axis for - // as long as they are relatively positioned with alignment STRETCH or - // FLEX_START. Children that are immediately stacked in the initial loop - // will not be touched again in . - var/*bool*/ isSimpleStackCross = true; - var/*int*/ firstComplexCross = childCount; + var/*float*/ totalFlexGrowFactors = 0; + var/*float*/ totalFlexShrinkScaledFactors = 0; - var/*css_node_t**/ firstFlexChild = null; - var/*css_node_t**/ currentFlexChild = null; + i = startOfLineIndex; - var/*float*/ mainDim = leadingPaddingAndBorderMain; - var/*float*/ crossDim = 0; + // Maintain a linked list of the child nodes that can shrink and/or grow. + var/*css_node_t**/ firstRelativeChild = undefined; + var/*css_node_t**/ currentRelativeChild = undefined; - var/*float*/ maxWidth = CSS_UNDEFINED; - var/*float*/ maxHeight = CSS_UNDEFINED; - for (i = startLine; i < childCount; ++i) { + // Add items to the current line until it's full or we run out of items. + while (i < childCount) { child = node.children[i]; - child.lineIndex = linesCount; + child.lineIndex = lineCount; - child.nextAbsoluteChild = null; - child.nextFlexChild = null; - - var/*css_align_t*/ alignItem = getAlignItem(node, child); - - // Pre-fill cross axis dimensions when the child is using stretch before - // we call the recursive layout pass - if (alignItem === CSS_ALIGN_STRETCH && - getPositionType(child) === CSS_POSITION_RELATIVE && - isCrossDimDefined && - !isStyleDimDefined(child, crossAxis)) { - child.layout[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, node.layout[dim[crossAxis]] - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - } else if (getPositionType(child) === CSS_POSITION_ABSOLUTE) { - // Store a private linked list of absolutely positioned children - // so that we can efficiently traverse them later. - if (firstAbsoluteChild === null) { - firstAbsoluteChild = child; + if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) { + var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis); + + // If this is a multi-line flow and this item pushes us over the available size, we've + // hit the end of the current line. Break out of the loop and lay out the current line. + if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) { + break; } - if (currentAbsoluteChild !== null) { - currentAbsoluteChild.nextAbsoluteChild = child; - } - currentAbsoluteChild = child; - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(child, axis) && - isPosDefined(child, leading[axis]) && - isPosDefined(child, trailing[axis])) { - child.layout[dim[axis]] = fmaxf( - boundAxis(child, axis, node.layout[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis])), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + sizeConsumedOnCurrentLine += outerFlexBasis; + itemsOnLine++; + + if (isFlex(child)) { + totalFlexGrowFactors += getFlexGrowFactor(child); + + // Unlike the grow factor, the shrink factor is scaled relative to the child + // dimension. + totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis; } + + // Store a private linked list of children that need to be layed out. + if (firstRelativeChild === undefined) { + firstRelativeChild = child; + } + if (currentRelativeChild !== undefined) { + currentRelativeChild.nextChild = child; + } + currentRelativeChild = child; + child.nextChild = undefined; } - - var/*float*/ nextContentDim = 0; - - // It only makes sense to consider a child flexible if we have a computed - // dimension for the node. - if (isMainDimDefined && isFlex(child)) { - flexibleChildrenCount++; - totalFlexible += child.style.flex; - - // Store a private linked list of flexible children so that we can - // efficiently traverse them later. - if (firstFlexChild === null) { - firstFlexChild = child; - } - if (currentFlexChild !== null) { - currentFlexChild.nextFlexChild = child; - } - currentFlexChild = child; - - // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information, which represents - // the smallest possible size for the child, to compute the remaining - // available space. - nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + - getMarginAxis(child, mainAxis); - - } else { - maxWidth = CSS_UNDEFINED; - maxHeight = CSS_UNDEFINED; - - if (!isMainRowDirection) { - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node.layout[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - } else { - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - } - - // This is the main recursive call. We layout non flexible children. - if (alreadyComputedNextLayout === 0) { - layoutNode(/*(java)!layoutContext, */child, maxWidth, maxHeight, direction); - } - - // Absolute positioned elements do not take part of the layout, so we - // don't use them to compute mainContentDim - if (getPositionType(child) === CSS_POSITION_RELATIVE) { - nonFlexibleChildrenCount++; - // At this point we know the final size and margin of the element. - nextContentDim = getDimWithMargin(child, mainAxis); - } - } - - // The element we are about to add would make us go to the next line - if (isNodeFlexWrap && - isMainDimDefined && - mainContentDim + nextContentDim > definedMainDim && - // If there's only one element, then it's bigger than the content - // and needs its own line - i !== startLine) { - nonFlexibleChildrenCount--; - alreadyComputedNextLayout = 1; - break; - } - - // Disable simple stacking in the main axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackMain && - (getPositionType(child) !== CSS_POSITION_RELATIVE || isFlex(child))) { - isSimpleStackMain = false; - firstComplexMain = i; - } - - // Disable simple stacking in the cross axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackCross && - (getPositionType(child) !== CSS_POSITION_RELATIVE || - (alignItem !== CSS_ALIGN_STRETCH && alignItem !== CSS_ALIGN_FLEX_START) || - (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) { - isSimpleStackCross = false; - firstComplexCross = i; - } - - if (isSimpleStackMain) { - child.layout[pos[mainAxis]] += mainDim; - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); - } - - mainDim += getDimWithMargin(child, mainAxis); - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); - } - - if (isSimpleStackCross) { - child.layout[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); - } - } - - alreadyComputedNextLayout = 0; - mainContentDim += nextContentDim; - endLine = i + 1; + + i++; + endOfLineIndex++; } - - // Layout flexible children and allocate empty space + + // If we don't need to measure the cross axis, we can skip the entire flex step. + var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY; // In order to position the elements in the main axis, we have two // controls. The space between the beginning and the first element @@ -823,212 +921,300 @@ var computeLayout = (function() { var/*float*/ leadingMainDim = 0; var/*float*/ betweenMainDim = 0; - // The remaining available space that needs to be allocated - var/*float*/ remainingMainDim = 0; - if (isMainDimDefined) { - remainingMainDim = definedMainDim - mainContentDim; - } else { - remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + var/*float*/ remainingFreeSpace = 0; + if (!isUndefined(availableInnerMainDim)) { + remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; + } else if (sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized based on its content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine; + } + + var/*float*/ remainingFreeSpaceAfterFlex = remainingFreeSpace; + + if (!canSkipFlex) { + var/*float*/ childFlexBasis; + var/*float*/ flexShrinkScaledFactor; + var/*float*/ flexGrowFactor; + var/*float*/ baseMainSize; + var/*float*/ boundMainSize; + + // Do two passes over the flex items to figure out how to distribute the remaining space. + // The first pass finds the items whose min/max constraints trigger, freezes them at those + // sizes, and excludes those sizes from the remaining space. The second pass sets the size + // of each flexible item. It distributes the remaining space amongst the items whose min/max + // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing + // their min/max constraints to trigger again. + // + // This two pass approach for resolving min/max constraints deviates from the spec. The + // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process + // that needs to be repeated a variable number of times. The algorithm implemented here + // won't handle all cases but it was simpler to implement and it mitigates performance + // concerns because we know exactly how many passes it'll do. + + // First pass: detect the flex items whose min/max constraints trigger + var/*float*/ deltaFreeSpace = 0; + var/*float*/ deltaFlexShrinkScaledFactors = 0; + var/*float*/ deltaFlexGrowFactors = 0; + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild !== undefined) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor !== 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize !== boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; + } + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor !== 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize !== boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexGrowFactors -= flexGrowFactor; + } + } + } + + currentRelativeChild = currentRelativeChild.nextChild; + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; + totalFlexGrowFactors += deltaFlexGrowFactors; + remainingFreeSpace += deltaFreeSpace; + remainingFreeSpaceAfterFlex = remainingFreeSpace; + + // Second pass: resolve the sizes of the flexible items + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild !== undefined) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + var/*float*/ updatedMainSize = childFlexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor !== 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor); + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor !== 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor); + } + } + + remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + + if (isMainAxisRow) { + childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } else { + childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } + + var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) && + getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH; + + // Recursively call the layout algorithm for this child with the updated main size. + layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex'); + + currentRelativeChild = currentRelativeChild.nextChild; + } + } + + remainingFreeSpace = remainingFreeSpaceAfterFlex; + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main axis. + // Their dimensions are also set in the cross axis with the exception of items + // that are aligned 'stretch'. We need to compute these stretch values and + // set the final positions. + + // If we are using "at most" rules in the main axis, we won't distribute + // any remaining space at this point. + if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) { + remainingFreeSpace = 0; } - // If there are flexible children in the mix, they are going to fill the - // remaining space - if (flexibleChildrenCount !== 0) { - var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible; - var/*float*/ baseMainDim; - var/*float*/ boundMainDim; - - // If the flex share of remaining space doesn't meet min/max bounds, - // remove this child from flex calculations. - currentFlexChild = firstFlexChild; - while (currentFlexChild !== null) { - baseMainDim = flexibleMainDim * currentFlexChild.style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis); - boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); - - if (baseMainDim !== boundMainDim) { - remainingMainDim -= boundMainDim; - totalFlexible -= currentFlexChild.style.flex; - } - - currentFlexChild = currentFlexChild.nextFlexChild; - } - flexibleMainDim = remainingMainDim / totalFlexible; - - // The non flexible children can overflow the container, in this case - // we should just assume that there is no space available. - if (flexibleMainDim < 0) { - flexibleMainDim = 0; - } - - currentFlexChild = firstFlexChild; - while (currentFlexChild !== null) { - // At this point we know the final size of the element in the main - // dimension - currentFlexChild.layout[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, - flexibleMainDim * currentFlexChild.style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis) - ); - - maxWidth = CSS_UNDEFINED; - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node.layout[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - maxHeight = CSS_UNDEFINED; - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else if (isMainRowDirection) { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - - // And we recursively call the layout algorithm for this child - layoutNode(/*(java)!layoutContext, */currentFlexChild, maxWidth, maxHeight, direction); - - child = currentFlexChild; - currentFlexChild = currentFlexChild.nextFlexChild; - child.nextFlexChild = null; - } - - // We use justifyContent to figure out how to allocate the remaining - // space available - } else if (justifyContent !== CSS_JUSTIFY_FLEX_START) { + // Use justifyContent to figure out how to allocate the remaining space + // available in the main axis. + if (justifyContent !== CSS_JUSTIFY_FLEX_START) { if (justifyContent === CSS_JUSTIFY_CENTER) { - leadingMainDim = remainingMainDim / 2; + leadingMainDim = remainingFreeSpace / 2; } else if (justifyContent === CSS_JUSTIFY_FLEX_END) { - leadingMainDim = remainingMainDim; + leadingMainDim = remainingFreeSpace; } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) { - remainingMainDim = fmaxf(remainingMainDim, 0); - if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 !== 0) { - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + remainingFreeSpace = fmaxf(remainingFreeSpace, 0); + if (itemsOnLine > 1) { + betweenMainDim = remainingFreeSpace / (itemsOnLine - 1); } else { betweenMainDim = 0; } } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) { // Space on the edges is half of the space between elements - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount); + betweenMainDim = remainingFreeSpace / itemsOnLine; leadingMainDim = betweenMainDim / 2; } } - // Position elements in the main axis and compute dimensions + var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim; + var/*float*/ crossDim = 0; - // At this point, all the children have their dimensions set. We need to - // find their position. In order to do that, we accumulate data in - // variables that are also useful to compute the total dimensions of the - // container! - mainDim += leadingMainDim; - - for (i = firstComplexMain; i < endLine; ++i) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { child = node.children[i]; if (getPositionType(child) === CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { - // In case the child is position absolute and has left/top being - // defined, we override the position to whatever the user said - // (and margin/border). - child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + - getLeadingBorder(node, mainAxis) + - getLeadingMargin(child, mainAxis); - } else { - // If the child is position absolute (without top/left) or relative, - // we put it at the current accumulated offset. - child.layout[pos[mainAxis]] += mainDim; - - // Define the trailing position accordingly. - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + + getLeadingBorder(node, mainAxis) + + getLeadingMargin(child, mainAxis); } - - // Now that we placed the element, we need to update the variables - // We only need to do that for relative elements. Absolute elements + } else { + if (performLayout) { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child.layout[pos[mainAxis]] += mainDim; + } + + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements // do not take part in that phase. if (getPositionType(child) === CSS_POSITION_RELATIVE) { - // The main dimension is the sum of all the elements dimension plus - // the spacing. - mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); - // The cross dimension is the max of the elements dimension since there - // can only be one element in that cross dimension. - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims because + // they weren't computed. This means we can't call getDimWithMargin. + mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis; + crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); + + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis)); + } } } } - var/*float*/ containerCrossAxis = node.layout[dim[crossAxis]]; - if (!isCrossDimDefined) { - containerCrossAxis = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); + mainDim += trailingPaddingAndBorderMain; + + var/*float*/ containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; + + if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) { + containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim); + } } - // Position elements in the cross axis - for (i = firstComplexCross; i < endLine; ++i) { - child = node.children[i]; + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) { + crossDim = availableInnerCrossDim; + } - if (getPositionType(child) === CSS_POSITION_ABSOLUTE && - isPosDefined(child, leading[crossAxis])) { - // In case the child is absolutely positionned and has a - // top/left/bottom/right being set, we override all the previously - // computed positions to set it correctly. - child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + - getLeadingBorder(node, crossAxis) + - getLeadingMargin(child, crossAxis); + // Clamp to the min/max size specified on the container. + crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; - } else { - var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross; + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { + child = node.children[i]; - // For a relative children, we're either using alignItems (parent) or - // alignSelf (child) in order to determine the position in the cross axis - if (getPositionType(child) === CSS_POSITION_RELATIVE) { - /*eslint-disable */ - // This variable is intentionally re-defined as the code is transpiled to a block scope language + if (getPositionType(child) === CSS_POSITION_ABSOLUTE) { + // If the child is absolutely positioned and has a top/left/bottom/right + // set, override all the previously computed positions to set it correctly. + if (isPosDefined(child, leading[crossAxis])) { + child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + + getLeadingBorder(node, crossAxis) + + getLeadingMargin(child, crossAxis); + } else { + child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross + + getLeadingMargin(child, crossAxis); + } + } else { + var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis var/*css_align_t*/ alignItem = getAlignItem(node, child); - /*eslint-enable */ + + // If the child uses align stretch, we need to lay it out one more time, this time + // forcing the cross-axis size to be the computed cross size for the current line. if (alignItem === CSS_ALIGN_STRETCH) { - // You can only stretch if the dimension has not already been defined - // previously. - if (!isStyleDimDefined(child, crossAxis)) { - var/*float*/ dimCrossAxis = child.layout[dim[crossAxis]]; - child.layout[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, containerCrossAxis - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - - // If the size has changed, and this child has children we need to re-layout this child - if (dimCrossAxis != child.layout[dim[crossAxis]] && child.children.length > 0) { - // Reset child margins before re-layout as they are added back in layoutNode and would be doubled - child.layout[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child.layout[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child.layout[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - child.layout[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - - layoutNode(/*(java)!layoutContext, */child, maxWidth, maxHeight, direction); - } + childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + var/*bool*/ isCrossSizeDefinite = false; + + if (isMainAxisRow) { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN); + childHeight = crossDim; + } else { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW); + childWidth = crossDim; + } + + // If the child defines a definite size for its cross axis, there's no need to stretch. + if (!isCrossSizeDefinite) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch'); } } else if (alignItem !== CSS_ALIGN_FLEX_START) { - // The remaining space between the parent dimensions+padding and child - // dimensions+margin. - var/*float*/ remainingCrossDim = containerCrossAxis - - paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis); + var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis); if (alignItem === CSS_ALIGN_CENTER) { leadingCrossDim += remainingCrossDim / 2; @@ -1036,41 +1222,25 @@ var computeLayout = (function() { leadingCrossDim += remainingCrossDim; } } - } - // And we apply the position - child.layout[pos[crossAxis]] += linesCrossDim + leadingCrossDim; - - // Define the trailing position accordingly. - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); + // And we apply the position + child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; } } } - linesCrossDim += crossDim; - linesMainDim = fmaxf(linesMainDim, mainDim); - linesCount += 1; - startLine = endLine; + totalLineCrossDim += crossDim; + maxLineMainDim = fmaxf(maxLineMainDim, mainDim); + + // Reset variables for new line. + lineCount++; + startOfLineIndex = endOfLineIndex; + endOfLineIndex = startOfLineIndex; } - // - // - // Note(prenaux): More than one line, we need to layout the crossAxis - // according to alignContent. - // - // Note that we could probably remove and handle the one line case - // here too, but for the moment this is safer since it won't interfere with - // previously working code. - // - // See specs: - // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm - // section 9.4 - // - if (linesCount > 1 && isCrossDimDefined) { - var/*float*/ nodeCrossAxisInnerSize = node.layout[dim[crossAxis]] - - paddingAndBorderAxisCross; - var/*float*/ remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) { + var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; var/*float*/ crossDimLead = 0; var/*float*/ currentLead = leadingPaddingAndBorderCross; @@ -1081,19 +1251,20 @@ var computeLayout = (function() { } else if (alignContent === CSS_ALIGN_CENTER) { currentLead += remainingAlignContentDim / 2; } else if (alignContent === CSS_ALIGN_STRETCH) { - if (nodeCrossAxisInnerSize > linesCrossDim) { - crossDimLead = (remainingAlignContentDim / linesCount); + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = (remainingAlignContentDim / lineCount); } } var/*int*/ endIndex = 0; - for (i = 0; i < linesCount; ++i) { + for (i = 0; i < lineCount; ++i) { var/*int*/ startIndex = endIndex; + var/*int*/ j; // compute the line's height and find the endIndex var/*float*/ lineHeight = 0; - for (ii = startIndex; ii < childCount; ++ii) { - child = node.children[ii]; + for (j = startIndex; j < childCount; ++j) { + child = node.children[j]; if (getPositionType(child) !== CSS_POSITION_RELATIVE) { continue; } @@ -1101,33 +1272,33 @@ var computeLayout = (function() { break; } if (isLayoutDimDefined(child, crossAxis)) { - lineHeight = fmaxf( - lineHeight, - child.layout[dim[crossAxis]] + getMarginAxis(child, crossAxis) - ); + lineHeight = fmaxf(lineHeight, + child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis)); } } - endIndex = ii; + endIndex = j; lineHeight += crossDimLead; - for (ii = startIndex; ii < endIndex; ++ii) { - child = node.children[ii]; - if (getPositionType(child) !== CSS_POSITION_RELATIVE) { - continue; - } + if (performLayout) { + for (j = startIndex; j < endIndex; ++j) { + child = node.children[j]; + if (getPositionType(child) !== CSS_POSITION_RELATIVE) { + continue; + } - var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child); - if (alignContentAlignItem === CSS_ALIGN_FLEX_START) { - child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) { - child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[dim[crossAxis]]; - } else if (alignContentAlignItem === CSS_ALIGN_CENTER) { - var/*float*/ childHeight = child.layout[dim[crossAxis]]; - child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; - } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) { - child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - // TODO(prenaux): Correctly set the height of items with undefined - // (auto) crossAxis dimension. + var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem === CSS_ALIGN_FLEX_START) { + child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) { + child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]]; + } else if (alignContentAlignItem === CSS_ALIGN_CENTER) { + childHeight = child.layout[measuredDim[crossAxis]]; + child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) { + child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with indefinite + // (auto) crossAxis dimension. + } } } @@ -1135,138 +1306,266 @@ var computeLayout = (function() { } } - var/*bool*/ needsMainTrailingPos = false; - var/*bool*/ needsCrossTrailingPos = false; + // STEP 9: COMPUTING FINAL DIMENSIONS + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (!isMainDimDefined) { - node.layout[dim[mainAxis]] = fmaxf( - // We're missing the last padding at this point to get the final - // dimension - boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), - // We can never assign a width smaller than the padding and borders - paddingAndBorderAxisMain - ); + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim); + } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) { + node.layout[measuredDim[mainAxis]] = fmaxf( + fminf(availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)), + paddingAndBorderAxisMain); + } + + if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross); + } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) { + node.layout[measuredDim[crossAxis]] = fmaxf( + fminf(availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)), + paddingAndBorderAxisCross); + } + + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + if (performLayout) { + var/*bool*/ needsMainTrailingPos = false; + var/*bool*/ needsCrossTrailingPos = false; if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE || mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsMainTrailingPos = true; } - } - - if (!isCrossDimDefined) { - node.layout[dim[crossAxis]] = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE || crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsCrossTrailingPos = true; } - } - // Set trailing position if necessary - if (needsMainTrailingPos || needsCrossTrailingPos) { - for (i = 0; i < childCount; ++i) { - child = node.children[i]; + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node.children[i]; - if (needsMainTrailingPos) { - setTrailingPosition(node, child, mainAxis); - } + if (needsMainTrailingPos) { + setTrailingPosition(node, child, mainAxis); + } - if (needsCrossTrailingPos) { - setTrailingPosition(node, child, crossAxis); + if (needsCrossTrailingPos) { + setTrailingPosition(node, child, crossAxis); + } } } } - - // Calculate dimensions for absolutely positioned elements + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN currentAbsoluteChild = firstAbsoluteChild; - while (currentAbsoluteChild !== null) { - // Pre-fill dimensions when using absolute position and both offsets for - // the axis are defined (either both left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + while (currentAbsoluteChild !== undefined) { + // Now that we know the bounds of the container, perform layout again on the + // absolutely-positioned children. + if (performLayout) { - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(currentAbsoluteChild, axis) && - isPosDefined(currentAbsoluteChild, leading[axis]) && - isPosDefined(currentAbsoluteChild, trailing[axis])) { - currentAbsoluteChild.layout[dim[axis]] = fmaxf( - boundAxis(currentAbsoluteChild, axis, node.layout[dim[axis]] - - getBorderAxis(node, axis) - - getMarginAxis(currentAbsoluteChild, axis) - - getPosition(currentAbsoluteChild, leading[axis]) - - getPosition(currentAbsoluteChild, trailing[axis]) - ), - // You never want to go smaller than padding - getPaddingAndBorderAxis(currentAbsoluteChild, axis) - ); + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + } else { + // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) { + childWidth = node.layout.measuredWidth - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) - + (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]); + childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth); + } + } + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } else { + // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) { + childHeight = node.layout.measuredHeight - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) - + (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]); + childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight); + } } - if (isPosDefined(currentAbsoluteChild, trailing[axis]) && - !isPosDefined(currentAbsoluteChild, leading[axis])) { - currentAbsoluteChild.layout[leading[axis]] = - node.layout[dim[axis]] - - currentAbsoluteChild.layout[dim[axis]] - - getPosition(currentAbsoluteChild, trailing[axis]); + // If we're still missing one or the other dimension, measure the content. + if (isUndefined(childWidth) || isUndefined(childHeight)) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure'); + childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout'); + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) { + currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] = + node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] - + currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]); + } + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) { + currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] = + node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] - + currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]); } } - child = currentAbsoluteChild; - currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild; - child.nextAbsoluteChild = null; + currentAbsoluteChild = currentAbsoluteChild.nextChild; } } + + // + // This is a wrapper around the layoutNodeImpl function. It determines + // whether the layout request is redundant and can be skipped. + // + // Parameters: + // Input parameters are the same as layoutNodeImpl (see above) + // Return parameter is true if layout was performed, false if skipped + // + function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, + widthMeasureMode, heightMeasureMode, performLayout, reason) { + var layout = node.layout; - function layoutNode(node, parentMaxWidth, parentMaxHeight, parentDirection) { - node.shouldUpdate = true; + var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) || + layout.lastParentDirection !== parentDirection; - var direction = node.style.direction || CSS_DIRECTION_LTR; - var skipLayout = - !node.isDirty && - node.lastLayout && - node.lastLayout.requestedHeight === node.layout.height && - node.lastLayout.requestedWidth === node.layout.width && - node.lastLayout.parentMaxWidth === parentMaxWidth && - node.lastLayout.parentMaxHeight === parentMaxHeight && - node.lastLayout.direction === direction; - - if (skipLayout) { - node.layout.width = node.lastLayout.width; - node.layout.height = node.lastLayout.height; - node.layout.top = node.lastLayout.top; - node.layout.left = node.lastLayout.left; - } else { - if (!node.lastLayout) { - node.lastLayout = {}; + if (needToVisitNode) { + // Invalidate the cached results. + if (layout.cachedMeasurements !== undefined) { + layout.cachedMeasurements = []; } + if (layout.cachedLayout !== undefined) { + layout.cachedLayout.widthMeasureMode = undefined; + layout.cachedLayout.heightMeasureMode = undefined; + } + } - node.lastLayout.requestedWidth = node.layout.width; - node.lastLayout.requestedHeight = node.layout.height; - node.lastLayout.parentMaxWidth = parentMaxWidth; - node.lastLayout.parentMaxHeight = parentMaxHeight; - node.lastLayout.direction = direction; - - // Reset child layouts - node.children.forEach(function(child) { - child.layout.width = undefined; - child.layout.height = undefined; - child.layout.top = 0; - child.layout.left = 0; - }); - - layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection); - - node.lastLayout.width = node.layout.width; - node.lastLayout.height = node.layout.height; - node.lastLayout.top = node.layout.top; - node.lastLayout.left = node.layout.left; + var cachedResults; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the positions + // and dimensions for nodes in the subtree. The algorithm assumes that each node + // gets layed out a maximum of one time per tree layout, but multiple measurements + // may be required to resolve all of the flex dimensions. + if (performLayout) { + if (layout.cachedLayout && + layout.cachedLayout.availableWidth === availableWidth && + layout.cachedLayout.availableHeight === availableHeight && + layout.cachedLayout.widthMeasureMode === widthMeasureMode && + layout.cachedLayout.heightMeasureMode === heightMeasureMode) { + cachedResults = layout.cachedLayout; + } + } else if (layout.cachedMeasurements) { + for (var i = 0, len = layout.cachedMeasurements.length; i < len; i++) { + if (layout.cachedMeasurements[i].availableWidth === availableWidth && + layout.cachedMeasurements[i].availableHeight === availableHeight && + layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode && + layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) { + cachedResults = layout.cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults !== undefined) { + layout.measureWidth = cachedResults.computedWidth; + layout.measureHeight = cachedResults.computedHeight; + } else { + layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout); + layout.lastParentDirection = parentDirection; + + if (cachedResults === undefined) { + var newCacheEntry; + if (performLayout) { + // Use the single layout cache entry. + if (layout.cachedLayout === undefined) { + layout.cachedLayout = {}; + } + newCacheEntry = layout.cachedLayout; + } else { + // Allocate a new measurement cache entry. + if (layout.cachedMeasurements === undefined) { + layout.cachedMeasurements = []; + } + newCacheEntry = {}; + layout.cachedMeasurements.push(newCacheEntry); + } + + newCacheEntry.availableWidth = availableWidth; + newCacheEntry.availableHeight = availableHeight; + newCacheEntry.widthMeasureMode = widthMeasureMode; + newCacheEntry.heightMeasureMode = heightMeasureMode; + newCacheEntry.computedWidth = layout.measuredWidth; + newCacheEntry.computedHeight = layout.measuredHeight; + } + } + + if (performLayout) { + node.layout.width = node.layout.measuredWidth; + node.layout.height = node.layout.measuredHeight; + layout.shouldUpdate = true; + } + + layout.generationCount = gCurrentGenerationCount; + return (needToVisitNode || cachedResults === undefined); + } + + function layoutNode(node, availableWidth, availableHeight, parentDirection) { + // Increment the generation count. This will force the recursive routine to visit + // all dirty nodes at least once. Subsequent visits will be skipped if the input + // parameters don't change. + gCurrentGenerationCount++; + + // If the caller didn't specify a height/width, use the dimensions + // specified in the style. + if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + } + if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + } + + var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) { + setPosition(node, node.layout.direction); } } diff --git a/dist/css-layout.min.js b/dist/css-layout.min.js index 7ea18191..86e0108f 100644 --- a/dist/css-layout.min.js +++ b/dist/css-layout.min.js @@ -1,2 +1,2 @@ -!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.computeLayout=b()}(this,function(){var a=function(){function a(b){if((!b.layout||b.isDirty)&&(b.layout={width:void 0,height:void 0,top:0,left:0,right:0,bottom:0}),b.style||(b.style={}),b.children||(b.children=[]),b.style.measure&&b.children&&b.children.length)throw new Error("Using custom measure function is supported only for leaf nodes.");return b.children.forEach(a),b}function b(a){return void 0===a||isNaN(a)}function c(a){return a===Q||a===R}function d(a){return a===S||a===T}function e(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function f(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function g(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function h(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function i(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function j(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function k(a,b){return g(a,b)+i(a,b)}function l(a,b){return h(a,b)+j(a,b)}function m(a,b){return i(a,b)+j(a,b)}function n(a,b){return e(a,b)+f(a,b)}function o(a,b){return k(a,b)+l(a,b)}function p(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function q(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function r(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function s(a,b){if(b===P){if(a===Q)return R;if(a===R)return Q}return a}function t(a,b){var c;return c=a.style.direction?a.style.direction:N,c===N&&(c=void 0===b?O:b),c}function u(a){return a.style.flexDirection?a.style.flexDirection:S}function v(a,b){return d(a)?s(Q,b):S}function w(a){return a.style.position?a.style.position:"relative"}function x(a){return w(a)===ba&&a.style.flex>0}function y(a){return"wrap"===a.style.flexWrap}function z(a,b){return a.layout[ja[b]]+n(a,b)}function A(a,b){return void 0!==a.style[ja[b]]&&a.style[ja[b]]>=0}function B(a,b){return void 0!==a.layout[ja[b]]&&a.layout[ja[b]]>=0}function C(a,b){return void 0!==a.style[b]}function D(a){return void 0!==a.style.measure}function E(a,b){return void 0!==a.style[b]?a.style[b]:0}function F(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function G(a,b){return a>b?a:b}function H(a,b){B(a,b)||A(a,b)&&(a.layout[ja[b]]=G(F(a,b,a.style[ja[b]]),o(a,b)))}function I(a,b,c){b.layout[ha[c]]=a.layout[ja[c]]-b.layout[ja[c]]-b.layout[ia[c]]}function J(a,b){return void 0!==a.style[ga[b]]?E(a,ga[b]):-E(a,ha[b])}function K(a,d,g,h){var j=t(a,h),K=s(u(a),j),N=v(K,j),O=s(Q,j);H(a,K),H(a,N),a.layout.direction=j,a.layout[ga[K]]+=e(a,K)+J(a,K),a.layout[ha[K]]+=f(a,K)+J(a,K),a.layout[ga[N]]+=e(a,N)+J(a,N),a.layout[ha[N]]+=f(a,N)+J(a,N);var P=a.children.length,ka=o(a,O),la=o(a,S);if(D(a)){var ma=B(a,O),na=M,oa=da;A(a,O)?(na=a.style.width,oa=ea):ma?(na=a.layout[ja[O]],oa=ea):(na=d-n(a,O),oa=fa),na-=ka,b(na)&&(oa=da);var pa=M,qa=da;A(a,S)?(pa=a.style.height,qa=ea):B(a,S)?(pa=a.layout[ja[S]],qa=ea):(pa=g-n(a,O),qa=fa),pa-=o(a,S),b(pa)&&(qa=da);var ra=!A(a,O)&&!ma,sa=!A(a,S)&&b(a.layout[ja[S]]);if(ra||sa){var ta=a.style.measure(na,oa,pa,qa);ra&&(a.layout.width=ta.width+ka),sa&&(a.layout.height=ta.height+la)}if(0===P)return}var ua,va,wa,xa,ya=y(a),za=p(a),Aa=k(a,K),Ba=k(a,N),Ca=o(a,K),Da=o(a,N),Ea=B(a,K),Fa=B(a,N),Ga=c(K),Ha=null,Ia=null,Ja=M;Ea&&(Ja=a.layout[ja[K]]-Ca);for(var Ka=0,La=0,Ma=0,Na=0,Oa=0,Pa=0;P>La;){var Qa=0,Ra=0,Sa=0,Ta=0,Ua=Ea&&za===U||!Ea&&za!==V,Va=Ua?P:Ka,Wa=!0,Xa=P,Ya=null,Za=null,$a=Aa,_a=0,ab=M,bb=M;for(ua=Ka;P>ua;++ua){wa=a.children[ua],wa.lineIndex=Pa,wa.nextAbsoluteChild=null,wa.nextFlexChild=null;var cb=r(a,wa);if(cb===aa&&w(wa)===ba&&Fa&&!A(wa,N))wa.layout[ja[N]]=G(F(wa,N,a.layout[ja[N]]-Da-n(wa,N)),o(wa,N));else if(w(wa)===ca)for(null===Ha&&(Ha=wa),null!==Ia&&(Ia.nextAbsoluteChild=wa),Ia=wa,va=0;2>va;va++)xa=0!==va?Q:S,B(a,xa)&&!A(wa,xa)&&C(wa,ga[xa])&&C(wa,ha[xa])&&(wa.layout[ja[xa]]=G(F(wa,xa,a.layout[ja[xa]]-o(a,xa)-n(wa,xa)-E(wa,ga[xa])-E(wa,ha[xa])),o(wa,xa)));var db=0;if(Ea&&x(wa)?(Ra++,Sa+=wa.style.flex,null===Ya&&(Ya=wa),null!==Za&&(Za.nextFlexChild=wa),Za=wa,db=o(wa,K)+n(wa,K)):(ab=M,bb=M,Ga?bb=B(a,S)?a.layout[ja[S]]-la:g-n(a,S)-la:ab=B(a,O)?a.layout[ja[O]]-ka:d-n(a,O)-ka,0===Ma&&L(wa,ab,bb,j),w(wa)===ba&&(Ta++,db=z(wa,K))),ya&&Ea&&Qa+db>Ja&&ua!==Ka){Ta--,Ma=1;break}Ua&&(w(wa)!==ba||x(wa))&&(Ua=!1,Va=ua),Wa&&(w(wa)!==ba||cb!==aa&&cb!==Z||cb==aa&&!Fa)&&(Wa=!1,Xa=ua),Ua&&(wa.layout[ia[K]]+=$a,Ea&&I(a,wa,K),$a+=z(wa,K),_a=G(_a,F(wa,N,z(wa,N)))),Wa&&(wa.layout[ia[N]]+=Na+Ba,Fa&&I(a,wa,N)),Ma=0,Qa+=db,La=ua+1}var eb=0,fb=0,gb=0;if(gb=Ea?Ja-Qa:G(Qa,0)-Qa,0!==Ra){var hb,ib,jb=gb/Sa;for(Za=Ya;null!==Za;)hb=jb*Za.style.flex+o(Za,K),ib=F(Za,K,hb),hb!==ib&&(gb-=ib,Sa-=Za.style.flex),Za=Za.nextFlexChild;for(jb=gb/Sa,0>jb&&(jb=0),Za=Ya;null!==Za;)Za.layout[ja[K]]=F(Za,K,jb*Za.style.flex+o(Za,K)),ab=M,B(a,O)?ab=a.layout[ja[O]]-ka:Ga||(ab=d-n(a,O)-ka),bb=M,B(a,S)?bb=a.layout[ja[S]]-la:Ga&&(bb=g-n(a,S)-la),L(Za,ab,bb,j),wa=Za,Za=Za.nextFlexChild,wa.nextFlexChild=null}else za!==U&&(za===V?eb=gb/2:za===W?eb=gb:za===X?(gb=G(gb,0),fb=Ra+Ta-1!==0?gb/(Ra+Ta-1):0):za===Y&&(fb=gb/(Ra+Ta),eb=fb/2));for($a+=eb,ua=Va;La>ua;++ua)wa=a.children[ua],w(wa)===ca&&C(wa,ga[K])?wa.layout[ia[K]]=E(wa,ga[K])+i(a,K)+e(wa,K):(wa.layout[ia[K]]+=$a,Ea&&I(a,wa,K),w(wa)===ba&&($a+=fb+z(wa,K),_a=G(_a,F(wa,N,z(wa,N)))));var kb=a.layout[ja[N]];for(Fa||(kb=G(F(a,N,_a+Da),Da)),ua=Xa;La>ua;++ua)if(wa=a.children[ua],w(wa)===ca&&C(wa,ga[N]))wa.layout[ia[N]]=E(wa,ga[N])+i(a,N)+e(wa,N);else{var lb=Ba;if(w(wa)===ba){var cb=r(a,wa);if(cb===aa){if(!A(wa,N)){var mb=wa.layout[ja[N]];wa.layout[ja[N]]=G(F(wa,N,kb-Da-n(wa,N)),o(wa,N)),mb!=wa.layout[ja[N]]&&wa.children.length>0&&(wa.layout[ga[K]]-=e(wa,K)+J(wa,K),wa.layout[ha[K]]-=f(wa,K)+J(wa,K),wa.layout[ga[N]]-=e(wa,N)+J(wa,N),wa.layout[ha[N]]-=f(wa,N)+J(wa,N),L(wa,ab,bb,j))}}else if(cb!==Z){var nb=kb-Da-z(wa,N);lb+=cb===$?nb/2:nb}}wa.layout[ia[N]]+=Na+lb,Fa&&I(a,wa,N)}Na+=_a,Oa=G(Oa,$a),Pa+=1,Ka=La}if(Pa>1&&Fa){var ob=a.layout[ja[N]]-Da,pb=ob-Na,qb=0,rb=Ba,sb=q(a);sb===_?rb+=pb:sb===$?rb+=pb/2:sb===aa&&ob>Na&&(qb=pb/Pa);var tb=0;for(ua=0;Pa>ua;++ua){var ub=tb,vb=0;for(va=ub;P>va;++va)if(wa=a.children[va],w(wa)===ba){if(wa.lineIndex!==ua)break;B(wa,N)&&(vb=G(vb,wa.layout[ja[N]]+n(wa,N)))}for(tb=va,vb+=qb,va=ub;tb>va;++va)if(wa=a.children[va],w(wa)===ba){var wb=r(a,wa);if(wb===Z)wa.layout[ia[N]]=rb+e(wa,N);else if(wb===_)wa.layout[ia[N]]=rb+vb-f(wa,N)-wa.layout[ja[N]];else if(wb===$){var xb=wa.layout[ja[N]];wa.layout[ia[N]]=rb+(vb-xb)/2}else wb===aa&&(wa.layout[ia[N]]=rb+e(wa,N))}rb+=vb}}var yb=!1,zb=!1;if(Ea||(a.layout[ja[K]]=G(F(a,K,Oa+l(a,K)),Ca),(K===R||K===T)&&(yb=!0)),Fa||(a.layout[ja[N]]=G(F(a,N,Na+Da),Da),(N===R||N===T)&&(zb=!0)),yb||zb)for(ua=0;P>ua;++ua)wa=a.children[ua],yb&&I(a,wa,K),zb&&I(a,wa,N);for(Ia=Ha;null!==Ia;){for(va=0;2>va;va++)xa=0!==va?Q:S,B(a,xa)&&!A(Ia,xa)&&C(Ia,ga[xa])&&C(Ia,ha[xa])&&(Ia.layout[ja[xa]]=G(F(Ia,xa,a.layout[ja[xa]]-m(a,xa)-n(Ia,xa)-E(Ia,ga[xa])-E(Ia,ha[xa])),o(Ia,xa))),C(Ia,ha[xa])&&!C(Ia,ga[xa])&&(Ia.layout[ga[xa]]=a.layout[ja[xa]]-Ia.layout[ja[xa]]-E(Ia,ha[xa]));wa=Ia,Ia=Ia.nextAbsoluteChild,wa.nextAbsoluteChild=null}}function L(a,b,c,d){a.shouldUpdate=!0;var e=a.style.direction||O,f=!a.isDirty&&a.lastLayout&&a.lastLayout.requestedHeight===a.layout.height&&a.lastLayout.requestedWidth===a.layout.width&&a.lastLayout.parentMaxWidth===b&&a.lastLayout.parentMaxHeight===c&&a.lastLayout.direction===e;f?(a.layout.width=a.lastLayout.width,a.layout.height=a.lastLayout.height,a.layout.top=a.lastLayout.top,a.layout.left=a.lastLayout.left):(a.lastLayout||(a.lastLayout={}),a.lastLayout.requestedWidth=a.layout.width,a.lastLayout.requestedHeight=a.layout.height,a.lastLayout.parentMaxWidth=b,a.lastLayout.parentMaxHeight=c,a.lastLayout.direction=e,a.children.forEach(function(a){a.layout.width=void 0,a.layout.height=void 0,a.layout.top=0,a.layout.left=0}),K(a,b,c,d),a.lastLayout.width=a.layout.width,a.lastLayout.height=a.layout.height,a.lastLayout.top=a.layout.top,a.lastLayout.left=a.layout.left)}var M,N="inherit",O="ltr",P="rtl",Q="row",R="row-reverse",S="column",T="column-reverse",U="flex-start",V="center",W="flex-end",X="space-between",Y="space-around",Z="flex-start",$="center",_="flex-end",aa="stretch",ba="relative",ca="absolute",da="undefined",ea="exactly",fa="at-most",ga={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},ha={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},ia={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},ja={row:"width","row-reverse":"width",column:"height","column-reverse":"height"};return{layoutNodeImpl:K,computeLayout:L,fillNodes:a}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); +!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.computeLayout=b()}(this,function(){var a=function(){function a(b){if(b.layout&&!b.isDirty||(b.layout={width:void 0,height:void 0,top:0,left:0,right:0,bottom:0}),b.style||(b.style={}),b.children||(b.children=[]),b.style.measure&&b.children&&b.children.length)throw new Error("Using custom measure function is supported only for leaf nodes.");return b.children.forEach(a),b}function b(a){return void 0===a||Number.isNaN(a)}function c(a){return a===ca||a===da}function d(a){return a===ea||a===fa}function e(a){return void 0===a.style.flex?0:a.style.flex}function f(a){return V?!0:e(a)<=0}function g(a){return e(a)>0?e(a):0}function h(a){if(V){if(0!==e(a))return 1}else if(e(a)<0)return 1;return 0}function i(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function j(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function k(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function l(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function m(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function n(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function o(a,b){return k(a,b)+m(a,b)}function p(a,b){return l(a,b)+n(a,b)}function q(a,b){return i(a,b)+j(a,b)}function r(a,b){return o(a,b)+p(a,b)}function s(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function t(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function u(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function v(a,b){if(b===ba){if(a===ca)return da;if(a===da)return ca}return a}function w(a,b){var c;return c=a.style.direction?a.style.direction:_,c===_&&(c=void 0===b?aa:b),c}function x(a){return a.style.flexDirection?a.style.flexDirection:ea}function y(a,b){return d(a)?v(ca,b):ea}function z(a){return a.style.position?a.style.position:pa}function A(a){return a.style.overflow?a.style.overflow:ra}function B(a){return z(a)===pa&&void 0!==a.style.flex&&0!==a.style.flex}function C(a){return"wrap"===a.style.flexWrap}function D(a,b){return a.layout[Aa[b]]+q(a,b)}function E(a,b){return void 0!==a.style[za[b]]&&a.style[za[b]]>=0}function F(a,b){return void 0!==a.layout[Aa[b]]&&a.layout[Aa[b]]>=0}function G(a,b){return void 0!==a.style[b]}function H(a){return void 0!==a.style.measure}function I(a,b){return void 0!==a.style[b]?a.style[b]:0}function J(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function K(a,b){return b>a?a:b}function L(a,b){return a>b?a:b}function M(a,b,c){return L(J(a,b,c),r(a,b))}function N(a,b,c){var d=z(b)===qa?0:b.layout[Aa[c]];b.layout[xa[c]]=a.layout[Aa[c]]-d-b.layout[ya[c]]}function O(a,b){return void 0!==a.style[wa[b]]?I(a,wa[b]):-I(a,xa[b])}function P(a,b){var c=v(x(a),b),d=y(c,b);a.layout[wa[c]]=i(a,c)+O(a,c),a.layout[xa[c]]=j(a,c)+O(a,c),a.layout[wa[d]]=i(a,d)+O(a,d),a.layout[xa[d]]=j(a,d)+O(a,d)}function Q(a,b){if(!a)throw new Error(b)}function R(a,d,e,k,l,O,R){Q(b(d)?l===ta:!0,"availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED"),Q(b(e)?O===ta:!0,"availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED");var T=r(a,ca),V=r(a,ea),W=q(a,ca),_=q(a,ea),aa=w(a,k);if(a.layout.direction=aa,H(a)){var ba=d-W-T,ra=e-_-V;if(l===ua&&O===ua)a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_);else if(0>=ba)a.layout.measuredWidth=M(a,ca,0),a.layout.measuredHeight=M(a,ea,0);else{var za=a.style.measure(ba,l,ra,O);a.layout.measuredWidth=M(a,ca,l===ta||l===va?za.width+T:d-W),a.layout.measuredHeight=M(a,ea,O===ta||O===va?za.height+V:e-_)}}else{var Ba=a.children.length;if(0===Ba)return a.layout.measuredWidth=M(a,ca,l===ta||l===va?T:d-W),void(a.layout.measuredHeight=M(a,ea,O===ta||O===va?V:e-_));if(!R){if(l===va&&0>=d&&O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,0));if(l===va&&0>=d)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,b(e)?0:e-_));if(O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,b(d)?0:d-W),void(a.layout.measuredHeight=M(a,ea,0));if(l===ua&&O===ua)return a.layout.measuredWidth=M(a,ca,d-W),void(a.layout.measuredHeight=M(a,ea,e-_))}var Ca,Da,Ea,Fa,Ga,Ha,Ia=v(x(a),aa),Ja=y(Ia,aa),Ka=c(Ia),La=s(a),Ma=C(a),Na=void 0,Oa=void 0,Pa=o(a,Ia),Qa=p(a,Ia),Ra=o(a,Ja),Sa=r(a,Ia),Ta=r(a,Ja),Ua=Ka?l:O,Va=Ka?O:l,Wa=d-W-T,Xa=e-_-V,Ya=Ka?Wa:Xa,Za=Ka?Xa:Wa;for(Da=0;Ba>Da;Da++){if(Ca=a.children[Da],R){var $a=w(Ca,aa);P(Ca,$a)}z(Ca)===qa?(void 0===Na&&(Na=Ca),void 0!==Oa&&(Oa.nextChild=Ca),Oa=Ca,Ca.nextChild=void 0):Ka&&E(Ca,ca)?Ca.layout.flexBasis=L(Ca.style.width,r(Ca,ca)):!Ka&&E(Ca,ea)?Ca.layout.flexBasis=L(Ca.style.height,r(Ca,ea)):f(Ca)||b(Ya)?(Ea=U,Fa=U,Ga=ta,Ha=ta,E(Ca,ca)&&(Ea=Ca.style.width+q(Ca,ca),Ga=ua),E(Ca,ea)&&(Fa=Ca.style.height+q(Ca,ea),Ha=ua),Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Ca,Ea,Fa,aa,Ga,Ha,!1,"measure"),Ca.layout.flexBasis=L(Ka?Ca.layout.measuredWidth:Ca.layout.measuredHeight,r(Ca,Ia))):Ca.layout.flexBasis=L(0,r(Ca,Ia))}for(var _a=0,ab=0,bb=0,cb=0,db=0;Ba>ab;){var eb=0,fb=0,gb=0,hb=0;Da=_a;for(var ib=void 0,jb=void 0;Ba>Da;){if(Ca=a.children[Da],Ca.lineIndex=bb,z(Ca)!==qa){var kb=Ca.layout.flexBasis+q(Ca,Ia);if(fb+kb>Ya&&Ma&&eb>0)break;fb+=kb,eb++,B(Ca)&&(gb+=g(Ca),hb+=h(Ca)*Ca.layout.flexBasis),void 0===ib&&(ib=Ca),void 0!==jb&&(jb.nextChild=Ca),jb=Ca,Ca.nextChild=void 0}Da++,ab++}var lb=!R&&Va===ua,mb=0,nb=0,ob=0;b(Ya)?0>fb&&(ob=-fb):ob=Ya-fb;var pb=ob;if(!lb){var qb,rb,sb,tb,ub,vb=0,wb=0,xb=0;for(jb=ib;void 0!==jb;)qb=jb.layout.flexBasis,0>ob?(rb=h(jb)*qb,0!==rb&&(tb=qb+ob/hb*rb,ub=M(jb,Ia,tb),tb!==ub&&(vb-=ub,wb-=rb))):ob>0&&(sb=g(jb),0!==sb&&(tb=qb+ob/gb*sb,ub=M(jb,Ia,tb),tb!==ub&&(vb-=ub,xb-=sb))),jb=jb.nextChild;for(hb+=wb,gb+=xb,ob+=vb,pb=ob,jb=ib;void 0!==jb;){qb=jb.layout.flexBasis;var yb=qb;0>ob?(rb=h(jb)*qb,0!==rb&&(yb=M(jb,Ia,qb+ob/hb*rb))):ob>0&&(sb=g(jb),0!==sb&&(yb=M(jb,Ia,qb+ob/gb*sb))),pb-=yb-qb,Ka?(Ea=yb+q(jb,ca),Ga=ua,E(jb,ea)?(Fa=jb.style.height+q(jb,ea),Ha=ua):(Fa=Za,Ha=b(Fa)?ta:va)):(Fa=yb+q(jb,ea),Ha=ua,E(jb,ca)?(Ea=jb.style.width+q(jb,ca),Ga=ua):(Ea=Za,Ga=b(Ea)?ta:va));var zb=!E(jb,Ja)&&u(a,jb)===oa;S(jb,Ea,Fa,aa,Ga,Ha,R&&!zb,"flex"),jb=jb.nextChild}}ob=pb,Ua===va&&(ob=0),La!==ga&&(La===ha?mb=ob/2:La===ia?mb=ob:La===ja?(ob=L(ob,0),nb=eb>1?ob/(eb-1):0):La===ka&&(nb=ob/eb,mb=nb/2));var Ab=Pa+mb,Bb=0;for(Da=_a;ab>Da;++Da)Ca=a.children[Da],z(Ca)===qa&&G(Ca,wa[Ia])?R&&(Ca.layout[ya[Ia]]=I(Ca,wa[Ia])+m(a,Ia)+i(Ca,Ia)):(R&&(Ca.layout[ya[Ia]]+=Ab),z(Ca)===pa&&(lb?(Ab+=nb+q(Ca,Ia)+Ca.layout.flexBasis,Bb=Za):(Ab+=nb+D(Ca,Ia),Bb=L(Bb,D(Ca,Ja)))));Ab+=Qa;var Cb=Za;if(Va!==ta&&Va!==va||(Cb=M(a,Ja,Bb+Ta)-Ta,Va===va&&(Cb=K(Cb,Za))),Ma||Va!==ua||(Bb=Za),Bb=M(a,Ja,Bb+Ta)-Ta,R)for(Da=_a;ab>Da;++Da)if(Ca=a.children[Da],z(Ca)===qa)G(Ca,wa[Ja])?Ca.layout[ya[Ja]]=I(Ca,wa[Ja])+m(a,Ja)+i(Ca,Ja):Ca.layout[ya[Ja]]=Ra+i(Ca,Ja);else{var Db=Ra,Eb=u(a,Ca);if(Eb===oa){Ea=Ca.layout.measuredWidth+q(Ca,ca),Fa=Ca.layout.measuredHeight+q(Ca,ea);var Fb=!1;Ka?(Fb=E(Ca,ea),Fa=Bb):(Fb=E(Ca,ca),Ea=Bb),Fb||(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,S(Ca,Ea,Fa,aa,Ga,Ha,!0,"stretch"))}else if(Eb!==la){var Gb=Cb-D(Ca,Ja);Db+=Eb===ma?Gb/2:Gb}Ca.layout[ya[Ja]]+=cb+Db}cb+=Bb,db=L(db,Ab),bb++,_a=ab,ab=_a}if(bb>1&&R&&!b(Za)){var Hb=Za-cb,Ib=0,Jb=Ra,Kb=t(a);Kb===na?Jb+=Hb:Kb===ma?Jb+=Hb/2:Kb===oa&&Za>cb&&(Ib=Hb/bb);var Lb=0;for(Da=0;bb>Da;++Da){var Mb,Nb=Lb,Ob=0;for(Mb=Nb;Ba>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){if(Ca.lineIndex!==Da)break;F(Ca,Ja)&&(Ob=L(Ob,Ca.layout[Aa[Ja]]+q(Ca,Ja)))}if(Lb=Mb,Ob+=Ib,R)for(Mb=Nb;Lb>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){var Pb=u(a,Ca);Pb===la?Ca.layout[ya[Ja]]=Jb+i(Ca,Ja):Pb===na?Ca.layout[ya[Ja]]=Jb+Ob-j(Ca,Ja)-Ca.layout[Aa[Ja]]:Pb===ma?(Fa=Ca.layout[Aa[Ja]],Ca.layout[ya[Ja]]=Jb+(Ob-Fa)/2):Pb===oa&&(Ca.layout[ya[Ja]]=Jb+i(Ca,Ja))}Jb+=Ob}}if(a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_),Ua===ta?a.layout[Aa[Ia]]=M(a,Ia,db):Ua===va&&(a.layout[Aa[Ia]]=L(K(Ya+Sa,J(a,Ia,db)),Sa)),Va===ta?a.layout[Aa[Ja]]=M(a,Ja,cb+Ta):Va===va&&(a.layout[Aa[Ja]]=L(K(Za+Ta,J(a,Ja,cb+Ta)),Ta)),R){var Qb=!1,Rb=!1;if(Ia!==da&&Ia!==fa||(Qb=!0),Ja!==da&&Ja!==fa||(Rb=!0),Qb||Rb)for(Da=0;Ba>Da;++Da)Ca=a.children[Da],Qb&&N(a,Ca,Ia),Rb&&N(a,Ca,Ja)}for(Oa=Na;void 0!==Oa;)R&&(Ea=U,Fa=U,E(Oa,ca)?Ea=Oa.style.width+q(Oa,ca):G(Oa,X)&&G(Oa,Z)&&(Ea=a.layout.measuredWidth-(m(a,ca)+n(a,ca))-(Oa.style[X]+Oa.style[Z]),Ea=M(Oa,ca,Ea)),E(Oa,ea)?Fa=Oa.style.height+q(Oa,ea):G(Oa,Y)&&G(Oa,$)&&(Fa=a.layout.measuredHeight-(m(a,ea)+n(a,ea))-(Oa.style[Y]+Oa.style[$]),Fa=M(Oa,ea,Fa)),(b(Ea)||b(Fa))&&(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Oa,Ea,Fa,aa,Ga,Ha,!1,"abs-measure"),Ea=Oa.layout.measuredWidth+q(Oa,ca),Fa=Oa.layout.measuredHeight+q(Oa,ea)),S(Oa,Ea,Fa,aa,ua,ua,!0,"abs-layout"),G(Oa,xa[ca])&&!G(Oa,wa[ca])&&(Oa.layout[wa[ca]]=a.layout[Aa[ca]]-Oa.layout[Aa[ca]]-I(Oa,xa[ca])),G(Oa,xa[ea])&&!G(Oa,wa[ea])&&(Oa.layout[wa[ea]]=a.layout[Aa[ea]]-Oa.layout[Aa[ea]]-I(Oa,xa[ea]))),Oa=Oa.nextChild}}function S(a,b,c,d,e,f,g,h){var i=a.layout,j=a.isDirty&&i.generationCount!==W||i.lastParentDirection!==d;j&&(void 0!==i.cachedMeasurements&&(i.cachedMeasurements=[]),void 0!==i.cachedLayout&&(i.cachedLayout.widthMeasureMode=void 0,i.cachedLayout.heightMeasureMode=void 0));var k;if(g)i.cachedLayout&&i.cachedLayout.availableWidth===b&&i.cachedLayout.availableHeight===c&&i.cachedLayout.widthMeasureMode===e&&i.cachedLayout.heightMeasureMode===f&&(k=i.cachedLayout);else if(i.cachedMeasurements)for(var l=0,m=i.cachedMeasurements.length;m>l;l++)if(i.cachedMeasurements[l].availableWidth===b&&i.cachedMeasurements[l].availableHeight===c&&i.cachedMeasurements[l].widthMeasureMode===e&&i.cachedMeasurements[l].heightMeasureMode===f){k=i.cachedMeasurements[l];break}if(j||void 0===k){if(R(a,b,c,d,e,f,g),i.lastParentDirection=d,void 0===k){var n;g?(void 0===i.cachedLayout&&(i.cachedLayout={}),n=i.cachedLayout):(void 0===i.cachedMeasurements&&(i.cachedMeasurements=[]),n={},i.cachedMeasurements.push(n)),n.availableWidth=b,n.availableHeight=c,n.widthMeasureMode=e,n.heightMeasureMode=f,n.computedWidth=i.measuredWidth,n.computedHeight=i.measuredHeight}}else i.measureWidth=k.computedWidth,i.measureHeight=k.computedHeight;return g&&(a.layout.width=a.layout.measuredWidth,a.layout.height=a.layout.measuredHeight,i.shouldUpdate=!0),i.generationCount=W,j||void 0===k}function T(a,c,d,e){W++,b(c)&&E(a,ca)&&(c=a.style.width+q(a,ca)),b(d)&&E(a,ea)&&(d=a.style.height+q(a,ea));var f=b(c)?ta:ua,g=b(d)?ta:ua;S(a,c,d,e,f,g,!0,"initial")&&P(a,a.layout.direction)}var U,V=!1,W=0,X="left",Y="top",Z="right",$="bottom",_="inherit",aa="ltr",ba="rtl",ca="row",da="row-reverse",ea="column",fa="column-reverse",ga="flex-start",ha="center",ia="flex-end",ja="space-between",ka="space-around",la="flex-start",ma="center",na="flex-end",oa="stretch",pa="relative",qa="absolute",ra="visible",sa="hidden",ta="undefined",ua="exactly",va="at-most",wa={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},xa={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},ya={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},za={row:"width","row-reverse":"width",column:"height","column-reverse":"height"},Aa={row:"measuredWidth","row-reverse":"measuredWidth",column:"measuredHeight","column-reverse":"measuredHeight"};return{layoutNodeImpl:R,computeLayout:T,fillNodes:a}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); //# sourceMappingURL=css-layout.min.js.map \ No newline at end of file diff --git a/dist/css-layout.min.js.map b/dist/css-layout.min.js.map index 0d933772..58ab8daa 100644 --- a/dist/css-layout.min.js.map +++ b/dist/css-layout.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getBorderAxis","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","isFlex","CSS_POSITION_RELATIVE","flex","isFlexWrap","flexWrap","getDimWithMargin","dim","isStyleDimDefined","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxis","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fmaxf","a","b","setDimensionFromStyle","setTrailingPosition","trailing","getRelativePosition","leading","layoutNodeImpl","parentMaxWidth","parentMaxHeight","mainAxis","crossAxis","resolvedRowAxis","childCount","paddingAndBorderAxisResolvedRow","paddingAndBorderAxisColumn","isResolvedRowDimDefined","CSS_UNDEFINED","widthMode","CSS_MEASURE_MODE_UNDEFINED","CSS_MEASURE_MODE_EXACTLY","CSS_MEASURE_MODE_AT_MOST","heightMode","isRowUndefined","isColumnUndefined","measureDim","i","ii","isNodeFlexWrap","leadingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","isMainDimDefined","isCrossDimDefined","isMainRowDirection","firstAbsoluteChild","currentAbsoluteChild","definedMainDim","startLine","endLine","alreadyComputedNextLayout","linesCrossDim","linesMainDim","linesCount","mainContentDim","flexibleChildrenCount","totalFlexible","nonFlexibleChildrenCount","isSimpleStackMain","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","firstComplexMain","isSimpleStackCross","firstComplexCross","firstFlexChild","currentFlexChild","mainDim","crossDim","lineIndex","nextAbsoluteChild","nextFlexChild","alignItem","CSS_ALIGN_STRETCH","CSS_POSITION_ABSOLUTE","nextContentDim","layoutNode","CSS_ALIGN_FLEX_START","leadingMainDim","betweenMainDim","remainingMainDim","baseMainDim","boundMainDim","flexibleMainDim","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","containerCrossAxis","leadingCrossDim","dimCrossAxis","remainingCrossDim","CSS_ALIGN_CENTER","nodeCrossAxisInnerSize","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","startIndex","lineHeight","alignContentAlignItem","childHeight","needsMainTrailingPos","needsCrossTrailingPos","shouldUpdate","skipLayout","lastLayout","requestedHeight","requestedWidth"],"mappings":"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","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n\n var CSS_UNDEFINED;\n\n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n\n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getBorderAxis(node, axis) {\n return getLeadingBorder(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return 'relative';\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex > 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[dim[axis]] + getMarginAxis(node, axis);\n }\n\n function isStyleDimDefined(node, axis) {\n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n\n function isLayoutDimDefined(node, axis) {\n return node.layout[dim[axis]] !== undefined && node.layout[dim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n\n function boundAxis(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n\n // When the user specifically sets a value for width or height\n function setDimensionFromStyle(node, axis) {\n // The parent already computed us a width or height. We just skip it\n if (isLayoutDimDefined(node, axis)) {\n return;\n }\n // We only run if there's a width or height defined\n if (!isStyleDimDefined(node, axis)) {\n return;\n }\n\n // The dimensions can never be smaller than the padding and border\n node.layout[dim[axis]] = fmaxf(\n boundAxis(node, axis, node.style[dim[axis]]),\n getPaddingAndBorderAxis(node, axis)\n );\n }\n\n function setTrailingPosition(node, child, axis) {\n child.layout[trailing[axis]] = node.layout[dim[axis]] -\n child.layout[dim[axis]] - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n\n function layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, /*css_direction_t*/parentDirection) {\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n\n // Handle width and height style attributes\n setDimensionFromStyle(node, mainAxis);\n setDimensionFromStyle(node, crossAxis);\n\n // Set the resolved resolution in the node's layout\n node.layout.direction = direction;\n\n // The position is set by the parent, but we need to complete it with a\n // delta composed of the margin and left/top/right/bottom\n node.layout[leading[mainAxis]] += getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] += getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n\n // Inline immutable values from the target node to avoid excessive method\n // invocations during the layout calculation.\n var/*int*/ childCount = node.children.length;\n var/*float*/ paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n if (isMeasureDefined(node)) {\n var/*bool*/ isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis);\n\n var/*float*/ width = CSS_UNDEFINED;\n var/*css_measure_mode_t*/ widthMode = CSS_MEASURE_MODE_UNDEFINED;\n if (isStyleDimDefined(node, resolvedRowAxis)) {\n width = node.style.width;\n widthMode = CSS_MEASURE_MODE_EXACTLY;\n } else if (isResolvedRowDimDefined) {\n width = node.layout[dim[resolvedRowAxis]];\n widthMode = CSS_MEASURE_MODE_EXACTLY;\n } else {\n width = parentMaxWidth -\n getMarginAxis(node, resolvedRowAxis);\n widthMode = CSS_MEASURE_MODE_AT_MOST;\n }\n width -= paddingAndBorderAxisResolvedRow;\n if (isUndefined(width)) {\n widthMode = CSS_MEASURE_MODE_UNDEFINED;\n }\n\n var/*float*/ height = CSS_UNDEFINED;\n var/*css_measure_mode_t*/ heightMode = CSS_MEASURE_MODE_UNDEFINED;\n if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n height = node.style.height;\n heightMode = CSS_MEASURE_MODE_EXACTLY;\n } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n height = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]];\n heightMode = CSS_MEASURE_MODE_EXACTLY;\n } else {\n height = parentMaxHeight -\n getMarginAxis(node, resolvedRowAxis);\n heightMode = CSS_MEASURE_MODE_AT_MOST;\n }\n height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n if (isUndefined(height)) {\n heightMode = CSS_MEASURE_MODE_UNDEFINED;\n }\n\n // We only need to give a dimension for the text if we haven't got any\n // for it computed yet. It can either be from the style attribute or because\n // the element is flexible.\n var/*bool*/ isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;\n var/*bool*/ isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&\n isUndefined(node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]]);\n\n // Let's not measure the text if we already know both dimensions\n if (isRowUndefined || isColumnUndefined) {\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n width,\n widthMode,\n height,\n heightMode\n );\n if (isRowUndefined) {\n node.layout.width = measureDim.width +\n paddingAndBorderAxisResolvedRow;\n }\n if (isColumnUndefined) {\n node.layout.height = measureDim.height +\n paddingAndBorderAxisColumn;\n }\n }\n if (childCount === 0) {\n return;\n }\n }\n\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n\n var/*bool*/ isMainDimDefined = isLayoutDimDefined(node, mainAxis);\n var/*bool*/ isCrossDimDefined = isLayoutDimDefined(node, crossAxis);\n var/*bool*/ isMainRowDirection = isRowDirection(mainAxis);\n\n var/*int*/ i;\n var/*int*/ ii;\n var/*css_node_t**/ child;\n var/*(c)!css_flex_direction_t*//*(java)!int*/ axis;\n\n var/*css_node_t**/ firstAbsoluteChild = null;\n var/*css_node_t**/ currentAbsoluteChild = null;\n\n var/*float*/ definedMainDim = CSS_UNDEFINED;\n if (isMainDimDefined) {\n definedMainDim = node.layout[dim[mainAxis]] - paddingAndBorderAxisMain;\n }\n\n // We want to execute the next two loops one per line with flex-wrap\n var/*int*/ startLine = 0;\n var/*int*/ endLine = 0;\n // var/*int*/ nextOffset = 0;\n var/*int*/ alreadyComputedNextLayout = 0;\n // We aggregate the total dimensions of the container in those two variables\n var/*float*/ linesCrossDim = 0;\n var/*float*/ linesMainDim = 0;\n var/*int*/ linesCount = 0;\n while (endLine < childCount) {\n // Layout non flexible children and count children by type\n\n // mainContentDim is accumulation of the dimensions and margin of all the\n // non flexible children. This will be used in order to either set the\n // dimensions of the node if none already exist, or to compute the\n // remaining space left for the flexible children.\n var/*float*/ mainContentDim = 0;\n\n // There are three kind of children, non flexible, flexible and absolute.\n // We need to know how many there are in order to distribute the space.\n var/*int*/ flexibleChildrenCount = 0;\n var/*float*/ totalFlexible = 0;\n var/*int*/ nonFlexibleChildrenCount = 0;\n\n // Use the line loop to position children in the main axis for as long\n // as they are using a simple stacking behaviour. Children that are\n // immediately stacked in the initial loop will not be touched again\n // in .\n var/*bool*/ isSimpleStackMain =\n (isMainDimDefined && justifyContent === CSS_JUSTIFY_FLEX_START) ||\n (!isMainDimDefined && justifyContent !== CSS_JUSTIFY_CENTER);\n var/*int*/ firstComplexMain = (isSimpleStackMain ? childCount : startLine);\n\n // Use the initial line loop to position children in the cross axis for\n // as long as they are relatively positioned with alignment STRETCH or\n // FLEX_START. Children that are immediately stacked in the initial loop\n // will not be touched again in .\n var/*bool*/ isSimpleStackCross = true;\n var/*int*/ firstComplexCross = childCount;\n\n var/*css_node_t**/ firstFlexChild = null;\n var/*css_node_t**/ currentFlexChild = null;\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain;\n var/*float*/ crossDim = 0;\n\n var/*float*/ maxWidth = CSS_UNDEFINED;\n var/*float*/ maxHeight = CSS_UNDEFINED;\n for (i = startLine; i < childCount; ++i) {\n child = node.children[i];\n child.lineIndex = linesCount;\n\n child.nextAbsoluteChild = null;\n child.nextFlexChild = null;\n\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n\n // Pre-fill cross axis dimensions when the child is using stretch before\n // we call the recursive layout pass\n if (alignItem === CSS_ALIGN_STRETCH &&\n getPositionType(child) === CSS_POSITION_RELATIVE &&\n isCrossDimDefined &&\n !isStyleDimDefined(child, crossAxis)) {\n child.layout[dim[crossAxis]] = fmaxf(\n boundAxis(child, crossAxis, node.layout[dim[crossAxis]] -\n paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),\n // You never want to go smaller than padding\n getPaddingAndBorderAxis(child, crossAxis)\n );\n } else if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === null) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== null) {\n currentAbsoluteChild.nextAbsoluteChild = child;\n }\n currentAbsoluteChild = child;\n\n // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both\n // left and right or top and bottom).\n for (ii = 0; ii < 2; ii++) {\n axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;\n if (isLayoutDimDefined(node, axis) &&\n !isStyleDimDefined(child, axis) &&\n isPosDefined(child, leading[axis]) &&\n isPosDefined(child, trailing[axis])) {\n child.layout[dim[axis]] = fmaxf(\n boundAxis(child, axis, node.layout[dim[axis]] -\n getPaddingAndBorderAxis(node, axis) -\n getMarginAxis(child, axis) -\n getPosition(child, leading[axis]) -\n getPosition(child, trailing[axis])),\n // You never want to go smaller than padding\n getPaddingAndBorderAxis(child, axis)\n );\n }\n }\n }\n\n var/*float*/ nextContentDim = 0;\n\n // It only makes sense to consider a child flexible if we have a computed\n // dimension for the node.\n if (isMainDimDefined && isFlex(child)) {\n flexibleChildrenCount++;\n totalFlexible += child.style.flex;\n\n // Store a private linked list of flexible children so that we can\n // efficiently traverse them later.\n if (firstFlexChild === null) {\n firstFlexChild = child;\n }\n if (currentFlexChild !== null) {\n currentFlexChild.nextFlexChild = child;\n }\n currentFlexChild = child;\n\n // Even if we don't know its exact size yet, we already know the padding,\n // border and margin. We'll use this partial information, which represents\n // the smallest possible size for the child, to compute the remaining\n // available space.\n nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +\n getMarginAxis(child, mainAxis);\n\n } else {\n maxWidth = CSS_UNDEFINED;\n maxHeight = CSS_UNDEFINED;\n\n if (!isMainRowDirection) {\n if (isLayoutDimDefined(node, resolvedRowAxis)) {\n maxWidth = node.layout[dim[resolvedRowAxis]] -\n paddingAndBorderAxisResolvedRow;\n } else {\n maxWidth = parentMaxWidth -\n getMarginAxis(node, resolvedRowAxis) -\n paddingAndBorderAxisResolvedRow;\n }\n } else {\n if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n maxHeight = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]] -\n paddingAndBorderAxisColumn;\n } else {\n maxHeight = parentMaxHeight -\n getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -\n paddingAndBorderAxisColumn;\n }\n }\n\n // This is the main recursive call. We layout non flexible children.\n if (alreadyComputedNextLayout === 0) {\n layoutNode(/*(java)!layoutContext, */child, maxWidth, maxHeight, direction);\n }\n\n // Absolute positioned elements do not take part of the layout, so we\n // don't use them to compute mainContentDim\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n nonFlexibleChildrenCount++;\n // At this point we know the final size and margin of the element.\n nextContentDim = getDimWithMargin(child, mainAxis);\n }\n }\n\n // The element we are about to add would make us go to the next line\n if (isNodeFlexWrap &&\n isMainDimDefined &&\n mainContentDim + nextContentDim > definedMainDim &&\n // If there's only one element, then it's bigger than the content\n // and needs its own line\n i !== startLine) {\n nonFlexibleChildrenCount--;\n alreadyComputedNextLayout = 1;\n break;\n }\n\n // Disable simple stacking in the main axis for the current line as\n // we found a non-trivial child. The remaining children will be laid out\n // in .\n if (isSimpleStackMain &&\n (getPositionType(child) !== CSS_POSITION_RELATIVE || isFlex(child))) {\n isSimpleStackMain = false;\n firstComplexMain = i;\n }\n\n // Disable simple stacking in the cross axis for the current line as\n // we found a non-trivial child. The remaining children will be laid out\n // in .\n if (isSimpleStackCross &&\n (getPositionType(child) !== CSS_POSITION_RELATIVE ||\n (alignItem !== CSS_ALIGN_STRETCH && alignItem !== CSS_ALIGN_FLEX_START) ||\n (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) {\n isSimpleStackCross = false;\n firstComplexCross = i;\n }\n\n if (isSimpleStackMain) {\n child.layout[pos[mainAxis]] += mainDim;\n if (isMainDimDefined) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n mainDim += getDimWithMargin(child, mainAxis);\n crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));\n }\n\n if (isSimpleStackCross) {\n child.layout[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;\n if (isCrossDimDefined) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n\n alreadyComputedNextLayout = 0;\n mainContentDim += nextContentDim;\n endLine = i + 1;\n }\n\n // Layout flexible children and allocate empty space\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // The remaining available space that needs to be allocated\n var/*float*/ remainingMainDim = 0;\n if (isMainDimDefined) {\n remainingMainDim = definedMainDim - mainContentDim;\n } else {\n remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim;\n }\n\n // If there are flexible children in the mix, they are going to fill the\n // remaining space\n if (flexibleChildrenCount !== 0) {\n var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible;\n var/*float*/ baseMainDim;\n var/*float*/ boundMainDim;\n\n // If the flex share of remaining space doesn't meet min/max bounds,\n // remove this child from flex calculations.\n currentFlexChild = firstFlexChild;\n while (currentFlexChild !== null) {\n baseMainDim = flexibleMainDim * currentFlexChild.style.flex +\n getPaddingAndBorderAxis(currentFlexChild, mainAxis);\n boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);\n\n if (baseMainDim !== boundMainDim) {\n remainingMainDim -= boundMainDim;\n totalFlexible -= currentFlexChild.style.flex;\n }\n\n currentFlexChild = currentFlexChild.nextFlexChild;\n }\n flexibleMainDim = remainingMainDim / totalFlexible;\n\n // The non flexible children can overflow the container, in this case\n // we should just assume that there is no space available.\n if (flexibleMainDim < 0) {\n flexibleMainDim = 0;\n }\n\n currentFlexChild = firstFlexChild;\n while (currentFlexChild !== null) {\n // At this point we know the final size of the element in the main\n // dimension\n currentFlexChild.layout[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,\n flexibleMainDim * currentFlexChild.style.flex +\n getPaddingAndBorderAxis(currentFlexChild, mainAxis)\n );\n\n maxWidth = CSS_UNDEFINED;\n if (isLayoutDimDefined(node, resolvedRowAxis)) {\n maxWidth = node.layout[dim[resolvedRowAxis]] -\n paddingAndBorderAxisResolvedRow;\n } else if (!isMainRowDirection) {\n maxWidth = parentMaxWidth -\n getMarginAxis(node, resolvedRowAxis) -\n paddingAndBorderAxisResolvedRow;\n }\n maxHeight = CSS_UNDEFINED;\n if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n maxHeight = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]] -\n paddingAndBorderAxisColumn;\n } else if (isMainRowDirection) {\n maxHeight = parentMaxHeight -\n getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -\n paddingAndBorderAxisColumn;\n }\n\n // And we recursively call the layout algorithm for this child\n layoutNode(/*(java)!layoutContext, */currentFlexChild, maxWidth, maxHeight, direction);\n\n child = currentFlexChild;\n currentFlexChild = currentFlexChild.nextFlexChild;\n child.nextFlexChild = null;\n }\n\n // We use justifyContent to figure out how to allocate the remaining\n // space available\n } else if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingMainDim / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingMainDim;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingMainDim = fmaxf(remainingMainDim, 0);\n if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 !== 0) {\n betweenMainDim = remainingMainDim /\n (flexibleChildrenCount + nonFlexibleChildrenCount - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingMainDim /\n (flexibleChildrenCount + nonFlexibleChildrenCount);\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n // Position elements in the main axis and compute dimensions\n\n // At this point, all the children have their dimensions set. We need to\n // find their position. In order to do that, we accumulate data in\n // variables that are also useful to compute the total dimensions of the\n // container!\n mainDim += leadingMainDim;\n\n for (i = firstComplexMain; i < endLine; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n } else {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n\n // Define the trailing position accordingly.\n if (isMainDimDefined) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n // Now that we placed the element, we need to update the variables\n // We only need to do that for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));\n }\n }\n }\n\n var/*float*/ containerCrossAxis = node.layout[dim[crossAxis]];\n if (!isCrossDimDefined) {\n containerCrossAxis = fmaxf(\n // For the cross dim, we add both sides at the end because the value\n // is aggregate via a max function. Intermediate negative values\n // can mess this computation otherwise\n boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),\n paddingAndBorderAxisCross\n );\n }\n\n // Position elements in the cross axis\n for (i = firstComplexCross; i < endLine; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[crossAxis])) {\n // In case the child is absolutely positionned and has a\n // top/left/bottom/right being set, we override all the previously\n // computed positions to set it correctly.\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n /*eslint-disable */\n // This variable is intentionally re-defined as the code is transpiled to a block scope language\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n /*eslint-enable */\n if (alignItem === CSS_ALIGN_STRETCH) {\n // You can only stretch if the dimension has not already been defined\n // previously.\n if (!isStyleDimDefined(child, crossAxis)) {\n var/*float*/ dimCrossAxis = child.layout[dim[crossAxis]];\n child.layout[dim[crossAxis]] = fmaxf(\n boundAxis(child, crossAxis, containerCrossAxis -\n paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),\n // You never want to go smaller than padding\n getPaddingAndBorderAxis(child, crossAxis)\n );\n\n // If the size has changed, and this child has children we need to re-layout this child\n if (dimCrossAxis != child.layout[dim[crossAxis]] && child.children.length > 0) {\n // Reset child margins before re-layout as they are added back in layoutNode and would be doubled\n child.layout[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) +\n getRelativePosition(child, mainAxis);\n child.layout[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) +\n getRelativePosition(child, mainAxis);\n child.layout[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) +\n getRelativePosition(child, crossAxis);\n child.layout[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) +\n getRelativePosition(child, crossAxis);\n\n layoutNode(/*(java)!layoutContext, */child, maxWidth, maxHeight, direction);\n }\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n // The remaining space between the parent dimensions+padding and child\n // dimensions+margin.\n var/*float*/ remainingCrossDim = containerCrossAxis -\n paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += linesCrossDim + leadingCrossDim;\n\n // Define the trailing position accordingly.\n if (isCrossDimDefined) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n\n linesCrossDim += crossDim;\n linesMainDim = fmaxf(linesMainDim, mainDim);\n linesCount += 1;\n startLine = endLine;\n }\n\n // \n //\n // Note(prenaux): More than one line, we need to layout the crossAxis\n // according to alignContent.\n //\n // Note that we could probably remove and handle the one line case\n // here too, but for the moment this is safer since it won't interfere with\n // previously working code.\n //\n // See specs:\n // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm\n // section 9.4\n //\n if (linesCount > 1 && isCrossDimDefined) {\n var/*float*/ nodeCrossAxisInnerSize = node.layout[dim[crossAxis]] -\n paddingAndBorderAxisCross;\n var/*float*/ remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (nodeCrossAxisInnerSize > linesCrossDim) {\n crossDimLead = (remainingAlignContentDim / linesCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < linesCount; ++i) {\n var/*int*/ startIndex = endIndex;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (ii = startIndex; ii < childCount; ++ii) {\n child = node.children[ii];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(\n lineHeight,\n child.layout[dim[crossAxis]] + getMarginAxis(child, crossAxis)\n );\n }\n }\n endIndex = ii;\n lineHeight += crossDimLead;\n\n for (ii = startIndex; ii < endIndex; ++ii) {\n child = node.children[ii];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[dim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n var/*float*/ childHeight = child.layout[dim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with undefined\n // (auto) crossAxis dimension.\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n // If the user didn't specify a width or height, and it has not been set\n // by the container, then we set it via the children.\n if (!isMainDimDefined) {\n node.layout[dim[mainAxis]] = fmaxf(\n // We're missing the last padding at this point to get the final\n // dimension\n boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)),\n // We can never assign a width smaller than the padding and borders\n paddingAndBorderAxisMain\n );\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n }\n\n if (!isCrossDimDefined) {\n node.layout[dim[crossAxis]] = fmaxf(\n // For the cross dim, we add both sides at the end because the value\n // is aggregate via a max function. Intermediate negative values\n // can mess this computation otherwise\n boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),\n paddingAndBorderAxisCross\n );\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n }\n\n // Set trailing position if necessary\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n\n // Calculate dimensions for absolutely positioned elements\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== null) {\n // Pre-fill dimensions when using absolute position and both offsets for\n // the axis are defined (either both left and right or top and bottom).\n for (ii = 0; ii < 2; ii++) {\n axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;\n\n if (isLayoutDimDefined(node, axis) &&\n !isStyleDimDefined(currentAbsoluteChild, axis) &&\n isPosDefined(currentAbsoluteChild, leading[axis]) &&\n isPosDefined(currentAbsoluteChild, trailing[axis])) {\n currentAbsoluteChild.layout[dim[axis]] = fmaxf(\n boundAxis(currentAbsoluteChild, axis, node.layout[dim[axis]] -\n getBorderAxis(node, axis) -\n getMarginAxis(currentAbsoluteChild, axis) -\n getPosition(currentAbsoluteChild, leading[axis]) -\n getPosition(currentAbsoluteChild, trailing[axis])\n ),\n // You never want to go smaller than padding\n getPaddingAndBorderAxis(currentAbsoluteChild, axis)\n );\n }\n\n if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&\n !isPosDefined(currentAbsoluteChild, leading[axis])) {\n currentAbsoluteChild.layout[leading[axis]] =\n node.layout[dim[axis]] -\n currentAbsoluteChild.layout[dim[axis]] -\n getPosition(currentAbsoluteChild, trailing[axis]);\n }\n }\n\n child = currentAbsoluteChild;\n currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;\n child.nextAbsoluteChild = null;\n }\n }\n\n function layoutNode(node, parentMaxWidth, parentMaxHeight, parentDirection) {\n node.shouldUpdate = true;\n\n var direction = node.style.direction || CSS_DIRECTION_LTR;\n var skipLayout =\n !node.isDirty &&\n node.lastLayout &&\n node.lastLayout.requestedHeight === node.layout.height &&\n node.lastLayout.requestedWidth === node.layout.width &&\n node.lastLayout.parentMaxWidth === parentMaxWidth &&\n node.lastLayout.parentMaxHeight === parentMaxHeight &&\n node.lastLayout.direction === direction;\n\n if (skipLayout) {\n node.layout.width = node.lastLayout.width;\n node.layout.height = node.lastLayout.height;\n node.layout.top = node.lastLayout.top;\n node.layout.left = node.lastLayout.left;\n } else {\n if (!node.lastLayout) {\n node.lastLayout = {};\n }\n\n node.lastLayout.requestedWidth = node.layout.width;\n node.lastLayout.requestedHeight = node.layout.height;\n node.lastLayout.parentMaxWidth = parentMaxWidth;\n node.lastLayout.parentMaxHeight = parentMaxHeight;\n node.lastLayout.direction = direction;\n\n // Reset child layouts\n node.children.forEach(function(child) {\n child.layout.width = undefined;\n child.layout.height = undefined;\n child.layout.top = 0;\n child.layout.left = 0;\n });\n\n layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection);\n\n node.lastLayout.width = node.layout.width;\n node.lastLayout.height = node.layout.height;\n node.lastLayout.top = node.layout.top;\n node.lastLayout.left = node.layout.left;\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file +{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","Number","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getFlex","flex","isFlexBasisAuto","POSITIVE_FLEX_IS_AUTO","getFlexGrowFactor","getFlexShrinkFactor","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","CSS_POSITION_RELATIVE","getOverflow","overflow","CSS_OVERFLOW_VISIBLE","isFlex","isFlexWrap","flexWrap","getDimWithMargin","measuredDim","isStyleDimDefined","dim","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxisWithinMinAndMax","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fminf","a","b","fmaxf","boundAxis","setTrailingPosition","size","CSS_POSITION_ABSOLUTE","trailing","getRelativePosition","leading","setPosition","mainAxis","crossAxis","assert","condition","message","layoutNodeImpl","availableWidth","availableHeight","widthMeasureMode","heightMeasureMode","performLayout","CSS_MEASURE_MODE_UNDEFINED","paddingAndBorderAxisRow","paddingAndBorderAxisColumn","marginAxisRow","marginAxisColumn","innerWidth","innerHeight","CSS_MEASURE_MODE_EXACTLY","measuredWidth","measuredHeight","measureDim","CSS_MEASURE_MODE_AT_MOST","childCount","i","childWidth","childHeight","childWidthMeasureMode","childHeightMeasureMode","isMainAxisRow","isNodeFlexWrap","firstAbsoluteChild","currentAbsoluteChild","leadingPaddingAndBorderMain","trailingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","measureModeMainDim","measureModeCrossDim","availableInnerWidth","availableInnerHeight","availableInnerMainDim","availableInnerCrossDim","childDirection","nextChild","flexBasis","CSS_UNDEFINED","CSS_OVERFLOW_HIDDEN","layoutNodeInternal","startOfLineIndex","endOfLineIndex","lineCount","totalLineCrossDim","maxLineMainDim","itemsOnLine","sizeConsumedOnCurrentLine","totalFlexGrowFactors","totalFlexShrinkScaledFactors","firstRelativeChild","currentRelativeChild","lineIndex","outerFlexBasis","canSkipFlex","leadingMainDim","betweenMainDim","remainingFreeSpace","remainingFreeSpaceAfterFlex","childFlexBasis","flexShrinkScaledFactor","flexGrowFactor","baseMainSize","boundMainSize","deltaFreeSpace","deltaFlexShrinkScaledFactors","deltaFlexGrowFactors","updatedMainSize","requiresStretchLayout","CSS_ALIGN_STRETCH","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","mainDim","crossDim","containerCrossAxis","leadingCrossDim","alignItem","isCrossSizeDefinite","CSS_ALIGN_FLEX_START","remainingCrossDim","CSS_ALIGN_CENTER","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","j","startIndex","lineHeight","alignContentAlignItem","needsMainTrailingPos","needsCrossTrailingPos","CSS_LEFT","CSS_RIGHT","CSS_TOP","CSS_BOTTOM","reason","needToVisitNode","generationCount","gCurrentGenerationCount","lastParentDirection","cachedMeasurements","cachedLayout","cachedResults","len","newCacheEntry","push","computedWidth","computedHeight","measureWidth","measureHeight","shouldUpdate","layoutNode"],"mappings":"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","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n \n var POSITIVE_FLEX_IS_AUTO = false;\n \n var gCurrentGenerationCount = 0;\n \n var CSS_UNDEFINED;\n \n var CSS_LEFT = 'left';\n var CSS_TOP = 'top';\n var CSS_RIGHT = 'right';\n var CSS_BOTTOM = 'bottom';\n \n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n \n var CSS_OVERFLOW_VISIBLE = 'visible';\n var CSS_OVERFLOW_HIDDEN = 'hidden';\n \n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n var measuredDim = {\n 'row': 'measuredWidth',\n 'row-reverse': 'measuredWidth',\n 'column': 'measuredHeight',\n 'column-reverse': 'measuredHeight'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || Number.isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n \n function getFlex(node) {\n if (node.style.flex === undefined) {\n return 0;\n }\n return node.style.flex;\n }\n \n function isFlexBasisAuto(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // All flex values are auto.\n return true;\n } else {\n // A flex value > 0 implies a basis of zero.\n return getFlex(node) <= 0;\n }\n }\n \n function getFlexGrowFactor(node) {\n // Flex grow is implied by positive values for flex.\n if (getFlex(node) > 0) {\n return getFlex(node);\n }\n return 0;\n }\n \n function getFlexShrinkFactor(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // A flex shrink factor of 1 is implied by non-zero values for flex.\n if (getFlex(node) !== 0) {\n return 1;\n }\n } else {\n // A flex shrink factor of 1 is implied by negative values for flex.\n if (getFlex(node) < 0) {\n return 1;\n }\n }\n return 0;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return CSS_POSITION_RELATIVE;\n }\n \n function getOverflow(node) {\n if (node.style.overflow) {\n return node.style.overflow;\n }\n return CSS_OVERFLOW_VISIBLE;\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex !== undefined && node.style.flex !== 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[measuredDim[axis]] + getMarginAxis(node, axis);\n }\n \n function isStyleDimDefined(node, axis) { \n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n \n function isLayoutDimDefined(node, axis) { \n return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n \n function boundAxisWithinMinAndMax(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n \n function fminf(a, b) {\n if (a < b) {\n return a;\n }\n return b;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n \n // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the\n // padding and border amount.\n function boundAxis(node, axis, value) {\n return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis));\n }\n\n function setTrailingPosition(node, child, axis) {\n var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ?\n 0 :\n child.layout[measuredDim[axis]];\n child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n \n function setPosition(node, direction) {\n var mainAxis = resolveAxis(getFlexDirection(node), direction);\n var crossAxis = getCrossFlexDirection(mainAxis, direction);\n \n node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n }\n \n function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n }\n \n //\n // This is the main routine that implements a subset of the flexbox layout algorithm\n // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.\n //\n // Limitations of this algorithm, compared to the full standard:\n // * Display property is always assumed to be 'flex' except for Text nodes, which\n // are assumed to be 'inline-flex'.\n // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are\n // stacked in document order.\n // * The 'order' property is not supported. The order of flex items is always defined\n // by document order.\n // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'\n // and 'hidden' are not supported.\n // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The\n // rarely-used 'wrap-reverse' is not supported.\n // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and\n // flexBasis, this algorithm supports only the three most common combinations:\n // flex: 0 is equiavlent to flex: 0 0 auto\n // flex: n (where n is a positive value) is equivalent to flex: n 1 auto\n // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0\n // This is faster because the content doesn't need to be measured, but it's\n // less flexible because the basis is always 0 and can't be overriden with\n // the width/height attributes.\n // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto\n // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel\n // values, and the default value is 0.\n // * The 'baseline' value is not supported for alignItems and alignSelf properties.\n // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be\n // specified as pixel values, not as percentages.\n // * There is no support for calculation of dimensions based on intrinsic aspect ratios\n // (e.g. images).\n // * There is no support for forced breaks.\n // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).\n //\n // Deviations from standard:\n // * Section 4.5 of the spec indicates that all flex items have a default minimum\n // main size. For text blocks, for example, this is the width of the widest word. \n // Calculating the minimum width is expensive, so we forego it and assume a default \n // minimum main size of 0.\n // * Min/Max sizes in the main axis are not honored when resolving flexible lengths.\n // * The spec indicates that the default value for 'flexDirection' is 'row', but\n // the algorithm below assumes a default of 'column'.\n //\n // Input parameters:\n // - node: current node to be sized and layed out\n // - availableWidth & availableHeight: available size to be used for sizing the node\n // or CSS_UNDEFINED if the size is not available; interpretation depends on layout\n // flags\n // - parentDirection: the inline (text) direction within the parent (left-to-right or\n // right-to-left)\n // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)\n // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)\n // - performLayout: specifies whether the caller is interested in just the dimensions\n // of the node or it requires the entire node and its subtree to be layed out\n // (with final positions)\n //\n // Details:\n // This routine is called recursively to lay out subtrees of flexbox elements. It uses the\n // information in node.style, which is treated as a read-only input. It is responsible for\n // setting the layout.direction and layout.measured_dimensions fields for the input node as well\n // as the layout.position and layout.line_index fields for its child nodes. The\n // layout.measured_dimensions field includes any border or padding for the node but does\n // not include margins.\n //\n // The spec describes four different layout modes: \"fill available\", \"max content\", \"min content\",\n // and \"fit content\". Of these, we don't use \"min content\" because we don't support default\n // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode\n // from the spec (https://www.w3.org/TR/css3-sizing/#terms):\n // - CSS_MEASURE_MODE_UNDEFINED: max content\n // - CSS_MEASURE_MODE_EXACTLY: fill available\n // - CSS_MEASURE_MODE_AT_MOST: fit content\n // \n // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of\n // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.\n //\n function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) {\n assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n \n var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n // Set the resolved resolution in the node's layout.\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n node.layout.direction = direction;\n\n // For content (text) nodes, determine the dimensions based on the text contents.\n if (isMeasureDefined(node)) {\n var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n \n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n\n // Don't bother sizing the text if both dimensions are already defined.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n } else if (innerWidth <= 0) {\n\n // Don't bother sizing the text if there's no horizontal space.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n } else {\n\n // Measure the text under the current constraints.\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n innerWidth,\n widthMeasureMode,\n innerHeight,\n heightMeasureMode\n );\n\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.width + paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.height + paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n }\n \n return;\n }\n\n // For nodes with no children, use the available values if they were provided, or\n // the minimum size as indicated by the padding and border sizes.\n var/*int*/ childCount = node.children.length;\n if (childCount === 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n return;\n }\n\n // If we're not being asked to perform a full layout, we can handle a number of common\n // cases here without incurring the cost of the remaining function.\n if (!performLayout) {\n // If we're being asked to size the content with an at most constraint but there is no available width,\n // the measurement will always be zero.\n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 &&\n heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn));\n return;\n }\n\n if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow));\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n // If we're being asked to use an exact width/height, there's no need to measure the children.\n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n return;\n }\n }\n\n // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*bool*/ isMainAxisRow = isRowDirection(mainAxis);\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_node_t**/ firstAbsoluteChild = undefined;\n var/*css_node_t**/ currentAbsoluteChild = undefined;\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n \n var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;\n var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;\n\n // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS\n var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;\n var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;\n\n // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM\n var/*css_node_t**/ child;\n var/*int*/ i;\n var/*float*/ childWidth;\n var/*float*/ childHeight;\n var/*css_measure_mode_t*/ childWidthMeasureMode;\n var/*css_measure_mode_t*/ childHeightMeasureMode;\n for (i = 0; i < childCount; i++) {\n child = node.children[i];\n\n if (performLayout) {\n // Set the initial position (relative to the parent).\n var/*css_direction_t*/ childDirection = resolveDirection(child, direction);\n setPosition(child, childDirection);\n }\n \n // Absolute-positioned children don't participate in flex layout. Add them\n // to a list that we can process later.\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === undefined) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== undefined) {\n currentAbsoluteChild.nextChild = child;\n }\n currentAbsoluteChild = child;\n child.nextChild = undefined;\n } else {\n \n if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n \n // The width is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW));\n } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n \n // The height is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN));\n } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) {\n \n // If the basis isn't 'auto', it is assumed to be zero.\n child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));\n } else {\n \n // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n \n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n // Measure the child\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure');\n \n child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis));\n }\n }\n }\n\n // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES\n \n // Indexes of children that represent the first and last items in the line.\n var/*int*/ startOfLineIndex = 0;\n var/*int*/ endOfLineIndex = 0;\n \n // Number of lines.\n var/*int*/ lineCount = 0;\n \n // Accumulated cross dimensions of all lines so far.\n var/*float*/ totalLineCrossDim = 0;\n\n // Max main dimension of all the lines.\n var/*float*/ maxLineMainDim = 0;\n\n while (endOfLineIndex < childCount) {\n \n // Number of items on the currently line. May be different than the difference\n // between start and end indicates because we skip over absolute-positioned items.\n var/*int*/ itemsOnLine = 0;\n\n // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin\n // of all the children on the current line. This will be used in order to\n // either set the dimensions of the node if none already exist or to compute\n // the remaining space left for the flexible children.\n var/*float*/ sizeConsumedOnCurrentLine = 0;\n\n var/*float*/ totalFlexGrowFactors = 0;\n var/*float*/ totalFlexShrinkScaledFactors = 0;\n\n i = startOfLineIndex;\n\n // Maintain a linked list of the child nodes that can shrink and/or grow.\n var/*css_node_t**/ firstRelativeChild = undefined;\n var/*css_node_t**/ currentRelativeChild = undefined;\n\n // Add items to the current line until it's full or we run out of items.\n while (i < childCount) {\n child = node.children[i];\n child.lineIndex = lineCount;\n\n if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) {\n var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis);\n \n // If this is a multi-line flow and this item pushes us over the available size, we've\n // hit the end of the current line. Break out of the loop and lay out the current line.\n if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {\n break;\n }\n\n sizeConsumedOnCurrentLine += outerFlexBasis;\n itemsOnLine++;\n\n if (isFlex(child)) {\n totalFlexGrowFactors += getFlexGrowFactor(child);\n \n // Unlike the grow factor, the shrink factor is scaled relative to the child\n // dimension.\n totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;\n }\n\n // Store a private linked list of children that need to be layed out.\n if (firstRelativeChild === undefined) {\n firstRelativeChild = child;\n }\n if (currentRelativeChild !== undefined) {\n currentRelativeChild.nextChild = child;\n }\n currentRelativeChild = child;\n child.nextChild = undefined;\n }\n \n i++;\n endOfLineIndex++;\n }\n \n // If we don't need to measure the cross axis, we can skip the entire flex step.\n var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY;\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS\n // Calculate the remaining available space that needs to be allocated.\n // If the main dimension size isn't known, it is computed based on\n // the line length, so there's no more space left to distribute.\n var/*float*/ remainingFreeSpace = 0;\n if (!isUndefined(availableInnerMainDim)) {\n remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;\n } else if (sizeConsumedOnCurrentLine < 0) {\n // availableInnerMainDim is indefinite which means the node is being sized based on its content.\n // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for\n // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.\n remainingFreeSpace = -sizeConsumedOnCurrentLine;\n }\n \n var/*float*/ remainingFreeSpaceAfterFlex = remainingFreeSpace;\n\n if (!canSkipFlex) {\n var/*float*/ childFlexBasis;\n var/*float*/ flexShrinkScaledFactor;\n var/*float*/ flexGrowFactor;\n var/*float*/ baseMainSize;\n var/*float*/ boundMainSize;\n \n // Do two passes over the flex items to figure out how to distribute the remaining space.\n // The first pass finds the items whose min/max constraints trigger, freezes them at those\n // sizes, and excludes those sizes from the remaining space. The second pass sets the size\n // of each flexible item. It distributes the remaining space amongst the items whose min/max\n // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing\n // their min/max constraints to trigger again. \n //\n // This two pass approach for resolving min/max constraints deviates from the spec. The\n // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process\n // that needs to be repeated a variable number of times. The algorithm implemented here\n // won't handle all cases but it was simpler to implement and it mitigates performance\n // concerns because we know exactly how many passes it'll do.\n \n // First pass: detect the flex items whose min/max constraints trigger\n var/*float*/ deltaFreeSpace = 0;\n var/*float*/ deltaFlexShrinkScaledFactors = 0;\n var/*float*/ deltaFlexGrowFactors = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize;\n deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;\n }\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize;\n deltaFlexGrowFactors -= flexGrowFactor;\n }\n }\n }\n \n currentRelativeChild = currentRelativeChild.nextChild;\n }\n \n totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;\n totalFlexGrowFactors += deltaFlexGrowFactors;\n remainingFreeSpace += deltaFreeSpace;\n remainingFreeSpaceAfterFlex = remainingFreeSpace;\n \n // Second pass: resolve the sizes of the flexible items\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n var/*float*/ updatedMainSize = childFlexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);\n }\n }\n \n remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis;\n \n if (isMainAxisRow) {\n childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = availableInnerCrossDim;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n } else {\n childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = availableInnerCrossDim;\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n }\n \n var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) &&\n getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH;\n\n // Recursively call the layout algorithm for this child with the updated main size.\n layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex');\n\n currentRelativeChild = currentRelativeChild.nextChild;\n }\n }\n \n remainingFreeSpace = remainingFreeSpaceAfterFlex;\n\n // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION\n\n // At this point, all the children have their dimensions set in the main axis.\n // Their dimensions are also set in the cross axis with the exception of items\n // that are aligned 'stretch'. We need to compute these stretch values and\n // set the final positions.\n\n // If we are using \"at most\" rules in the main axis, we won't distribute\n // any remaining space at this point.\n if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n remainingFreeSpace = 0;\n }\n\n // Use justifyContent to figure out how to allocate the remaining space\n // available in the main axis.\n if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingFreeSpace / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingFreeSpace;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingFreeSpace = fmaxf(remainingFreeSpace, 0);\n if (itemsOnLine > 1) {\n betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingFreeSpace / itemsOnLine;\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim;\n var/*float*/ crossDim = 0;\n\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n if (performLayout) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n }\n } else {\n if (performLayout) {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n }\n \n // Now that we placed the element, we need to update the variables.\n // We need to do that only for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n if (canSkipFlex) {\n // If we skipped the flex step, then we can't rely on the measuredDims because\n // they weren't computed. This means we can't call getDimWithMargin.\n mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis;\n crossDim = availableInnerCrossDim;\n } else {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n \n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n }\n }\n }\n }\n\n mainDim += trailingPaddingAndBorderMain;\n \n var/*float*/ containerCrossAxis = availableInnerCrossDim;\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n // Compute the cross axis from the max cross dimension of the children.\n containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n \n if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim);\n }\n }\n\n // If there's no flex wrap, the cross dimension is defined by the container.\n if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) {\n crossDim = availableInnerCrossDim;\n }\n\n // Clamp to the min/max size specified on the container.\n crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n\n // STEP 7: CROSS-AXIS ALIGNMENT\n // We can skip child alignment if we're just measuring the container.\n if (performLayout) {\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // If the child is absolutely positioned and has a top/left/bottom/right\n // set, override all the previously computed positions to set it correctly.\n if (isPosDefined(child, leading[crossAxis])) {\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n } else {\n child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross +\n getLeadingMargin(child, crossAxis);\n }\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n \n // If the child uses align stretch, we need to lay it out one more time, this time\n // forcing the cross-axis size to be the computed cross size for the current line.\n if (alignItem === CSS_ALIGN_STRETCH) {\n childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n var/*bool*/ isCrossSizeDefinite = false;\n \n if (isMainAxisRow) {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeight = crossDim;\n } else {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW);\n childWidth = crossDim;\n }\n \n // If the child defines a definite size for its cross axis, there's no need to stretch.\n if (!isCrossSizeDefinite) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch');\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;\n }\n }\n }\n\n totalLineCrossDim += crossDim;\n maxLineMainDim = fmaxf(maxLineMainDim, mainDim);\n\n // Reset variables for new line.\n lineCount++;\n startOfLineIndex = endOfLineIndex;\n endOfLineIndex = startOfLineIndex;\n }\n\n // STEP 8: MULTI-LINE CONTENT ALIGNMENT\n if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) {\n var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (availableInnerCrossDim > totalLineCrossDim) {\n crossDimLead = (remainingAlignContentDim / lineCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < lineCount; ++i) {\n var/*int*/ startIndex = endIndex;\n var/*int*/ j;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (j = startIndex; j < childCount; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(lineHeight,\n child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis));\n }\n }\n endIndex = j;\n lineHeight += crossDimLead;\n\n if (performLayout) {\n for (j = startIndex; j < endIndex; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n childHeight = child.layout[measuredDim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with indefinite\n // (auto) crossAxis dimension.\n }\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n // STEP 9: COMPUTING FINAL DIMENSIONS\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n\n // If the user didn't specify a width or height for the node, set the\n // dimensions based on the children.\n if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);\n } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[mainAxis]] = fmaxf(\n fminf(availableInnerMainDim + paddingAndBorderAxisMain,\n boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),\n paddingAndBorderAxisMain);\n }\n\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);\n } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[crossAxis]] = fmaxf(\n fminf(availableInnerCrossDim + paddingAndBorderAxisCross,\n boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),\n paddingAndBorderAxisCross);\n }\n \n // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN\n if (performLayout) {\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n\n // Set trailing position if necessary.\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n }\n \n // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== undefined) {\n // Now that we know the bounds of the container, perform layout again on the\n // absolutely-positioned children.\n if (performLayout) {\n\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n\n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n } else {\n // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) {\n childWidth = node.layout.measuredWidth -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) -\n (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]);\n childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);\n }\n }\n \n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n } else {\n // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) {\n childHeight = node.layout.measuredHeight -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) -\n (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]);\n childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);\n }\n }\n\n // If we're still missing one or the other dimension, measure the content.\n if (isUndefined(childWidth) || isUndefined(childHeight)) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure');\n childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout');\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]);\n }\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]);\n }\n }\n\n currentAbsoluteChild = currentAbsoluteChild.nextChild;\n }\n }\n \n //\n // This is a wrapper around the layoutNodeImpl function. It determines\n // whether the layout request is redundant and can be skipped.\n //\n // Parameters:\n // Input parameters are the same as layoutNodeImpl (see above)\n // Return parameter is true if layout was performed, false if skipped\n //\n function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection,\n widthMeasureMode, heightMeasureMode, performLayout, reason) {\n var layout = node.layout;\n\n var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) ||\n layout.lastParentDirection !== parentDirection;\n\n if (needToVisitNode) {\n // Invalidate the cached results.\n if (layout.cachedMeasurements !== undefined) {\n layout.cachedMeasurements = []; \n }\n if (layout.cachedLayout !== undefined) {\n layout.cachedLayout.widthMeasureMode = undefined;\n layout.cachedLayout.heightMeasureMode = undefined;\n }\n }\n\n var cachedResults;\n \n // Determine whether the results are already cached. We maintain a separate\n // cache for layouts and measurements. A layout operation modifies the positions\n // and dimensions for nodes in the subtree. The algorithm assumes that each node\n // gets layed out a maximum of one time per tree layout, but multiple measurements\n // may be required to resolve all of the flex dimensions.\n if (performLayout) {\n if (layout.cachedLayout &&\n layout.cachedLayout.availableWidth === availableWidth &&\n layout.cachedLayout.availableHeight === availableHeight &&\n layout.cachedLayout.widthMeasureMode === widthMeasureMode &&\n layout.cachedLayout.heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedLayout;\n }\n } else if (layout.cachedMeasurements) {\n for (var i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (layout.cachedMeasurements[i].availableWidth === availableWidth &&\n layout.cachedMeasurements[i].availableHeight === availableHeight &&\n layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode &&\n layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n \n if (!needToVisitNode && cachedResults !== undefined) {\n layout.measureWidth = cachedResults.computedWidth;\n layout.measureHeight = cachedResults.computedHeight;\n } else {\n layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);\n layout.lastParentDirection = parentDirection;\n \n if (cachedResults === undefined) {\n var newCacheEntry;\n if (performLayout) {\n // Use the single layout cache entry.\n if (layout.cachedLayout === undefined) {\n layout.cachedLayout = {};\n }\n newCacheEntry = layout.cachedLayout;\n } else {\n // Allocate a new measurement cache entry.\n if (layout.cachedMeasurements === undefined) {\n layout.cachedMeasurements = [];\n }\n newCacheEntry = {};\n layout.cachedMeasurements.push(newCacheEntry);\n }\n \n newCacheEntry.availableWidth = availableWidth;\n newCacheEntry.availableHeight = availableHeight;\n newCacheEntry.widthMeasureMode = widthMeasureMode;\n newCacheEntry.heightMeasureMode = heightMeasureMode;\n newCacheEntry.computedWidth = layout.measuredWidth;\n newCacheEntry.computedHeight = layout.measuredHeight;\n }\n }\n \n if (performLayout) {\n node.layout.width = node.layout.measuredWidth;\n node.layout.height = node.layout.measuredHeight;\n layout.shouldUpdate = true;\n }\n \n layout.generationCount = gCurrentGenerationCount;\n return (needToVisitNode || cachedResults === undefined);\n }\n \n function layoutNode(node, availableWidth, availableHeight, parentDirection) {\n // Increment the generation count. This will force the recursive routine to visit\n // all dirty nodes at least once. Subsequent visits will be skipped if the input\n // parameters don't change.\n gCurrentGenerationCount++;\n \n // If the caller didn't specify a height/width, use the dimensions\n // specified in the style.\n if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n }\n if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) {\n setPosition(node, node.layout.direction);\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file diff --git a/src/CSharpTranspiler.js b/src/CSharpTranspiler.js index 1342347a..b9ae21bb 100644 --- a/src/CSharpTranspiler.js +++ b/src/CSharpTranspiler.js @@ -9,11 +9,18 @@ function __transpileToCSharpCommon(code) { return code + .replace(/'abs-layout'/g, '"abs-layout"') + .replace(/'abs-measure'/g, '"abs-measure"') + .replace(/'flex'/g, '"flex"') + .replace(/'measure'/g, '"measure"') + .replace(/'stretch'/g, '"stretch"') + .replace(/undefined/g, 'null') .replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED') .replace(/CSS_JUSTIFY_/g, 'CSSJustify.') .replace(/CSS_MEASURE_MODE_/g, 'CSSMeasureMode.') .replace(/CSS_ALIGN_/g, 'CSSAlign.') .replace(/CSS_POSITION_/g, 'CSSPositionType.') + .replace(/CSS_OVERFLOW_/g, 'CSSOverflow.') .replace(/css_flex_direction_t/g, 'CSSFlexDirection') .replace(/css_direction_t/g, 'CSSDirection') .replace(/css_align_t/g, 'CSSAlign') @@ -21,6 +28,10 @@ function __transpileToCSharpCommon(code) { .replace(/css_measure_mode_t/g, 'CSSMeasureMode') .replace(/css_dim_t/g, 'MeasureOutput') .replace(/bool/g, 'boolean') + .replace(/style\[CSS_LEFT/g, 'style.position[POSITION_LEFT') + .replace(/style\[CSS_TOP/g, 'style.position[POSITION_TOP') + .replace(/style\[CSS_RIGHT/g, 'style.position[POSITION_RIGHT') + .replace(/style\[CSS_BOTTOM/g, 'style.position[POSITION_BOTTOM') .replace(/style\[dim/g, 'style.dimensions[dim') .replace(/(style|layout)\.width/g, '$1.dimensions[DIMENSION_WIDTH]') .replace(/(style|layout)\.height/g, '$1.dimensions[DIMENSION_HEIGHT]') @@ -28,22 +39,24 @@ function __transpileToCSharpCommon(code) { .replace(/layout\[pos/g, 'layout.position[pos') .replace(/layout\[leading/g, 'layout.position[leading') .replace(/layout\[trailing/g, 'layout.position[trailing') + .replace(/layout\[measuredDim/g, 'layout.measuredDimensions[dim') + .replace(/layout\.measuredWidth/g, 'layout.measuredDimensions[DIMENSION_WIDTH]') + .replace(/layout\.measuredHeight/g, 'layout.measuredDimensions[DIMENSION_HEIGHT]') .replace(/getPositionType\((.+?)\)/g, '$1.style.positionType') .replace(/getJustifyContent\((.+?)\)/g, '$1.style.justifyContent') .replace(/getAlignContent\((.+?)\)/g, '$1.style.alignContent') .replace(/isPosDefined\((.+?),\s*(.+?)\)/g, '!isUndefined\($1.style.position[$2]\)') - .replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.style.dimensions[dim[$2]]\) && $1.style.dimensions[dim[$2]] >= 0.0\)') - .replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.layout.dimensions[dim[$2]]\) && $1.layout.dimensions[dim[$2]] >= 0.0\)') + .replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '($1.style.dimensions[dim[$2]] >= 0.0)') + .replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '($1.layout.measuredDimensions[dim[$2]] >= 0.0)') .replace(/getPosition\((.+?),\s*(.+?)\)/g, '\(isUndefined\($1.style.position[$2]\) ? 0 : $1.style.position[$2]\)') - .replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.dimensions[dim[$3]] - $2.layout.dimensions[dim[$3]] - $2.layout.position[pos[$3]]') - .replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex > 0\)') + .replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.measuredDimensions[dim[$3]] - ($2.style.positionType == CSSPositionType.Absolute ? 0 : $2.layout.measuredDimensions[dim[$3]]) - $2.layout.position[pos[$3]]') + .replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex != 0\)') .replace(/isFlexWrap\((.+?)\)/g, '\($1.style.flexWrap == CSSWrap.WRAP\)') .replace(/getPaddingAndBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingPaddingAndBorder($1, $2) + getTrailingPaddingAndBorder($1, $2)\)') - .replace(/getBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingBorder($1, $2) + getTrailingBorder($1, $2)\)') .replace(/getMarginAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)') .replace(/getLeadingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getLeadingPadding($1, $2) + getLeadingBorder($1, $2)\)') .replace(/getTrailingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getTrailingPadding($1, $2) + getTrailingBorder($1, $2)\)') - .replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.dimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)') + .replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.measuredDimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)') .replace(/getLeadingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(leadingSpacing[$2], leading[$2])') .replace(/getTrailingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(trailingSpacing[$2], trailing[$2])') .replace(/getLeadingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(leadingSpacing[$2], leading[$2])') @@ -51,14 +64,18 @@ function __transpileToCSharpCommon(code) { .replace(/getLeadingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(leadingSpacing[$2], leading[$2])') .replace(/getTrailingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(trailingSpacing[$2], trailing[$2])') .replace(/isRowDirection\((.+?)\)/g, '\($1 == CSS_FLEX_DIRECTION_ROW || $1 == CSS_FLEX_DIRECTION_ROW_REVERSE\)') + .replace(/assert\((.+?),\s*'(.+?)'\)/g, 'Assertions.assertCondition($1, "$2")') .replace(/isUndefined\((.+?)\)/g, 'float.IsNaN\($1\)') + .replace(/getOverflow\((.+?)\)/g, '$1.style.overflow') + .replace(/layoutNodeInternal\((.+?)\)/g, 'layoutNodeInternal(layoutContext, $1)') + .replace(/style\.position\[CSS_/g, 'style.position[POSITION_') .replace(/\/\*\(c\)!([^*]+)\*\//g, '') .replace(/var\/\*\(java\)!([^*]+)\*\//g, '$1') .replace(/\/\*\(java\)!([^*]+)\*\//g, '$1') // additional case conversions - .replace(/(CSSConstants|CSSWrap|CSSJustify|CSSMeasureMode|CSSAlign|CSSPositionType)\.([_A-Z]+)/g, + .replace(/(CSSConstants|CSSWrap|CSSJustify|CSSMeasureMode|CSSAlign|CSSPositionType|CSSOverflow)\.([_A-Z]+)/g, function(str, match1, match2) { return match1 + '.' + constantToPascalCase(match2); }); @@ -139,12 +156,12 @@ var CSharpTranspiler = { transpileLayoutEngine: function(code) { return indent( __transpileToCSharpCommon(code) - .replace(/function\s+layoutNode.*/, '') .replace('node.style.measure', 'node.measure') .replace(/\.children\.length/g, '.getChildCount()') .replace(/node.children\[i\]/g, 'node.getChildAt(i)') - .replace(/node.children\[ii\]/g, 'node.getChildAt(ii)') + .replace(/node.children\[j\]/g, 'node.getChildAt(j)') .replace(/fmaxf/g, 'Math.Max') + .replace(/fminf/g, 'Math.Min') .replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages .replace(/var\/\*([^\/]+)\*\//g, '$1') .replace(/ === /g, ' == ') diff --git a/src/JavaTranspiler.js b/src/JavaTranspiler.js index 8623794f..c7ec14b1 100644 --- a/src/JavaTranspiler.js +++ b/src/JavaTranspiler.js @@ -9,11 +9,18 @@ function __transpileToJavaCommon(code) { return code + .replace(/'abs-layout'/g, '"abs-layout"') + .replace(/'abs-measure'/g, '"abs-measure"') + .replace(/'flex'/g, '"flex"') + .replace(/'measure'/g, '"measure"') + .replace(/'stretch'/g, '"stretch"') + .replace(/undefined/g, 'null') .replace(/CSS_UNDEFINED/g, 'CSSConstants.UNDEFINED') .replace(/CSS_JUSTIFY_/g, 'CSSJustify.') .replace(/CSS_MEASURE_MODE_/g, 'CSSMeasureMode.') .replace(/CSS_ALIGN_/g, 'CSSAlign.') .replace(/CSS_POSITION_/g, 'CSSPositionType.') + .replace(/CSS_OVERFLOW_/g, 'CSSOverflow.') .replace(/css_flex_direction_t/g, 'CSSFlexDirection') .replace(/css_direction_t/g, 'CSSDirection') .replace(/css_align_t/g, 'CSSAlign') @@ -21,6 +28,10 @@ function __transpileToJavaCommon(code) { .replace(/css_measure_mode_t/g, 'CSSMeasureMode') .replace(/css_dim_t/g, 'MeasureOutput') .replace(/bool/g, 'boolean') + .replace(/style\[CSS_LEFT/g, 'style.position[POSITION_LEFT') + .replace(/style\[CSS_TOP/g, 'style.position[POSITION_TOP') + .replace(/style\[CSS_RIGHT/g, 'style.position[POSITION_RIGHT') + .replace(/style\[CSS_BOTTOM/g, 'style.position[POSITION_BOTTOM') .replace(/style\[dim/g, 'style.dimensions[dim') .replace(/(style|layout)\.width/g, '$1.dimensions[DIMENSION_WIDTH]') .replace(/(style|layout)\.height/g, '$1.dimensions[DIMENSION_HEIGHT]') @@ -28,22 +39,24 @@ function __transpileToJavaCommon(code) { .replace(/layout\[pos/g, 'layout.position[pos') .replace(/layout\[leading/g, 'layout.position[leading') .replace(/layout\[trailing/g, 'layout.position[trailing') + .replace(/layout\[measuredDim/g, 'layout.measuredDimensions[dim') + .replace(/layout\.measuredWidth/g, 'layout.measuredDimensions[DIMENSION_WIDTH]') + .replace(/layout\.measuredHeight/g, 'layout.measuredDimensions[DIMENSION_HEIGHT]') .replace(/getPositionType\((.+?)\)/g, '$1.style.positionType') .replace(/getJustifyContent\((.+?)\)/g, '$1.style.justifyContent') .replace(/getAlignContent\((.+?)\)/g, '$1.style.alignContent') .replace(/isPosDefined\((.+?),\s*(.+?)\)/g, '!isUndefined\($1.style.position[$2]\)') - .replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.style.dimensions[dim[$2]]\) && $1.style.dimensions[dim[$2]] >= 0.0\)') - .replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '\(!isUndefined\($1.layout.dimensions[dim[$2]]\) && $1.layout.dimensions[dim[$2]] >= 0.0\)') + .replace(/isStyleDimDefined\((.+?),\s*(.+?)\)/g, '($1.style.dimensions[dim[$2]] >= 0.0)') + .replace(/isLayoutDimDefined\((.+?),\s*(.+?)\)/g, '($1.layout.measuredDimensions[dim[$2]] >= 0.0)') .replace(/getPosition\((.+?),\s*(.+?)\)/g, '\(isUndefined\($1.style.position[$2]\) ? 0 : $1.style.position[$2]\)') - .replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.dimensions[dim[$3]] - $2.layout.dimensions[dim[$3]] - $2.layout.position[pos[$3]]') - .replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex > 0\)') + .replace(/setTrailingPosition\((.+?),\s*(.+?),\s*(.+?)\)/g, '$2.layout.position[trailing[$3]] = $1.layout.measuredDimensions[dim[$3]] - ($2.style.positionType == CSSPositionType.ABSOLUTE ? 0 : $2.layout.measuredDimensions[dim[$3]]) - $2.layout.position[pos[$3]]') + .replace(/isFlex\((.+?)\)/g, '\($1.style.positionType == CSSPositionType.RELATIVE && $1.style.flex != 0\)') .replace(/isFlexWrap\((.+?)\)/g, '\($1.style.flexWrap == CSSWrap.WRAP\)') .replace(/getPaddingAndBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingPaddingAndBorder($1, $2) + getTrailingPaddingAndBorder($1, $2)\)') - .replace(/getBorderAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingBorder($1, $2) + getTrailingBorder($1, $2)\)') .replace(/getMarginAxis\((.+?),\s*(.+?)\)/g, '\(getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)') .replace(/getLeadingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getLeadingPadding($1, $2) + getLeadingBorder($1, $2)\)') .replace(/getTrailingPaddingAndBorder\((.+?),\s*(.+?)\)/g, '\(getTrailingPadding($1, $2) + getTrailingBorder($1, $2)\)') - .replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.dimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)') + .replace(/getDimWithMargin\((.+?),\s*(.+?)\)/g, '\($1.layout.measuredDimensions[dim[$2]] + getLeadingMargin($1, $2) + getTrailingMargin($1, $2)\)') .replace(/getLeadingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(leadingSpacing[$2], leading[$2])') .replace(/getTrailingMargin\((.+?),\s*(.+?)\)/g, '$1.style.margin.getWithFallback(trailingSpacing[$2], trailing[$2])') .replace(/getLeadingPadding\((.+?),\s*(.+?)\)/g, '$1.style.padding.getWithFallback(leadingSpacing[$2], leading[$2])') @@ -51,7 +64,11 @@ function __transpileToJavaCommon(code) { .replace(/getLeadingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(leadingSpacing[$2], leading[$2])') .replace(/getTrailingBorder\((.+?),\s*(.+?)\)/g, '$1.style.border.getWithFallback(trailingSpacing[$2], trailing[$2])') .replace(/isRowDirection\((.+?)\)/g, '\($1 == CSS_FLEX_DIRECTION_ROW || $1 == CSS_FLEX_DIRECTION_ROW_REVERSE\)') + .replace(/assert\((.+?),\s*'(.+?)'\)/g, 'Assertions.assertCondition($1, "$2")') .replace(/isUndefined\((.+?)\)/g, 'Float.isNaN\($1\)') + .replace(/getOverflow\((.+?)\)/g, '$1.style.overflow') + .replace(/layoutNodeInternal\((.+?)\)/g, 'layoutNodeInternal(layoutContext, $1)') + .replace(/style\.position\[CSS_/g, 'style.position[POSITION_') .replace(/\/\*\(c\)!([^*]+)\*\//g, '') .replace(/var\/\*\(java\)!([^*]+)\*\//g, '$1') .replace(/\/\*\(java\)!([^*]+)\*\//g, '$1'); @@ -118,12 +135,12 @@ var JavaTranspiler = { transpileLayoutEngine: function(code) { return indent( __transpileToJavaCommon(code) - .replace(/function\s+layoutNode.*/, '') .replace('node.style.measure', 'node.measure') .replace(/\.children\.length/g, '.getChildCount()') .replace(/node.children\[i\]/g, 'node.getChildAt(i)') - .replace(/node.children\[ii\]/g, 'node.getChildAt(ii)') + .replace(/node.children\[j\]/g, 'node.getChildAt(j)') .replace(/fmaxf/g, 'Math.max') + .replace(/fminf/g, 'Math.min') .replace(/\/\*\([^\/]+\*\/\n/g, '') // remove comments for other languages .replace(/var\/\*([^\/]+)\*\//g, '$1') .replace(/ === /g, ' == ') diff --git a/src/Layout-test-utils.js b/src/Layout-test-utils.js index 3195b09f..6af941e6 100644 --- a/src/Layout-test-utils.js +++ b/src/Layout-test-utils.js @@ -92,6 +92,7 @@ var layoutTestUtils = (function() { margin: 0; padding: 0; + min-width: 0; } hack to ignore three hundred px width of the body {} @@ -118,17 +119,24 @@ var layoutTestUtils = (function() { } function extractNodes(node) { - var layout = node.layout; - delete node.layout; + var keysToCopy = [ + 'width', + 'height', + 'left', + 'top' + ]; + var layout = {}; + keysToCopy.forEach(function(key) { + layout[key] = node.layout[key]; + }); + if (node.children && node.children.length > 0) { layout.children = node.children.map(extractNodes); } else { delete node.children; } - - delete layout.right; - delete layout.bottom; - delete layout.direction; + + delete node.layout; return layout; } @@ -183,13 +191,17 @@ var layoutTestUtils = (function() { function computeDOMLayout(node) { var body = getIframe().contentDocument.body; - + + function setStyle(div, name, value) { + div.style['-webkit-' + name] = value; + div.style['webkit' + capitalizeFirst(name)] = value; + div.style[name] = value; + } + function transfer(div, node, name, ext) { if (name in node.style) { var value = node.style[name] + (ext || ''); - div.style['-webkit-' + name] = value; - div.style['webkit' + capitalizeFirst(name)] = value; - div.style[name] = value; + setStyle(div, name, value); } } @@ -202,7 +214,19 @@ var layoutTestUtils = (function() { transfer(div, node, type + 'Start' + suffix, 'px'); transfer(div, node, type + 'End' + suffix, 'px'); } - + + function transferFlex(div, node) { + if ('flex' in node.style) { + var flex = node.style.flex; + var resolvedFlex = ( + flex < 0 ? '0 1 auto' : + flex > 0 ? (flex + ' 0 0') : + '0 0 auto' + ); + setStyle(div, 'flex', resolvedFlex); + } + } + function renderNode(parent, node) { var div = document.createElement('div'); transfer(div, node, 'width', 'px'); @@ -220,13 +244,14 @@ var layoutTestUtils = (function() { transferSpacing(div, node, 'border', 'Width'); transfer(div, node, 'flexDirection'); transfer(div, node, 'direction'); - transfer(div, node, 'flex'); + transferFlex(div, node); transfer(div, node, 'flexWrap'); transfer(div, node, 'justifyContent'); transfer(div, node, 'alignSelf'); transfer(div, node, 'alignItems'); transfer(div, node, 'alignContent'); transfer(div, node, 'position'); + transfer(div, node, 'overflow'); parent.appendChild(div); (node.children || []).forEach(function(child) { renderNode(div, child); diff --git a/src/Layout.c b/src/Layout.c index 1d03edf8..52acc085 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -7,6 +7,7 @@ * of patent rights can be found in the PATENTS file in the same directory. */ +#include #include #include #include @@ -29,6 +30,13 @@ __forceinline const float fmaxf(const float a, const float b) { #endif #endif +#define POSITIVE_FLEX_IS_AUTO 0 + +int gCurrentGenerationCount = 0; + +bool layoutNodeInternal(css_node_t* node, float availableWidth, float availableHeight, css_direction_t parentDirection, + css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, bool performLayout, char* reason); + bool isUndefined(float value) { return isnan(value); } @@ -40,12 +48,14 @@ static bool eq(float a, float b) { return fabs(a - b) < 0.0001; } -void init_css_node(css_node_t *node) { +void init_css_node(css_node_t* node) { node->style.align_items = CSS_ALIGN_STRETCH; node->style.align_content = CSS_ALIGN_FLEX_START; node->style.direction = CSS_DIRECTION_INHERIT; node->style.flex_direction = CSS_FLEX_DIRECTION_COLUMN; + + node->style.overflow = CSS_OVERFLOW_VISIBLE; // Some of the fields default to undefined and not 0 node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED; @@ -73,21 +83,23 @@ void init_css_node(css_node_t *node) { node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; // Such that the comparison is always going to be false - node->layout.last_requested_dimensions[CSS_WIDTH] = -1; - node->layout.last_requested_dimensions[CSS_HEIGHT] = -1; - node->layout.last_parent_max_width = -1; - node->layout.last_parent_max_height = -1; - node->layout.last_direction = (css_direction_t)-1; + node->layout.last_parent_direction = (css_direction_t)-1; node->layout.should_update = true; + node->layout.next_cached_measurements_index = 0; + + node->layout.measured_dimensions[CSS_WIDTH] = CSS_UNDEFINED; + node->layout.measured_dimensions[CSS_HEIGHT] = CSS_UNDEFINED; + node->layout.cached_layout.width_measure_mode = (css_measure_mode_t)-1; + node->layout.cached_layout.height_measure_mode = (css_measure_mode_t)-1; } -css_node_t *new_css_node() { - css_node_t *node = (css_node_t *)calloc(1, sizeof(*node)); +css_node_t* new_css_node() { + css_node_t* node = (css_node_t*)calloc(1, sizeof(*node)); init_css_node(node); return node; } -void free_css_node(css_node_t *node) { +void free_css_node(css_node_t* node) { free(node); } @@ -97,13 +109,13 @@ static void indent(int n) { } } -static void print_number_0(const char *str, float number) { +static void print_number_0(const char* str, float number) { if (!eq(number, 0)) { printf("%s: %g, ", str, number); } } -static void print_number_nan(const char *str, float number) { +static void print_number_nan(const char* str, float number) { if (!isnan(number)) { printf("%s: %g, ", str, number); } @@ -118,7 +130,7 @@ static bool four_equal(float four[4]) { static void print_css_node_rec( - css_node_t *node, + css_node_t* node, css_print_options_t options, int level ) { @@ -142,11 +154,11 @@ static void print_css_node_rec( if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN) { printf("flexDirection: 'column', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { - printf("flexDirection: 'columnReverse', "); + printf("flexDirection: 'column-reverse', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW) { printf("flexDirection: 'row', "); } else if (node->style.flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) { - printf("flexDirection: 'rowReverse', "); + printf("flexDirection: 'row-reverse', "); } if (node->style.justify_content == CSS_JUSTIFY_CENTER) { @@ -187,6 +199,12 @@ static void print_css_node_rec( print_number_nan("flex", node->style.flex); + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + printf("overflow: 'hidden', "); + } else if (node->style.overflow == CSS_OVERFLOW_VISIBLE) { + printf("overflow: 'visible', "); + } + if (four_equal(node->style.margin)) { print_number_0("margin", node->style.margin[CSS_LEFT]); } else { @@ -199,7 +217,7 @@ static void print_css_node_rec( } if (four_equal(node->style.padding)) { - print_number_0("padding", node->style.margin[CSS_LEFT]); + print_number_0("padding", node->style.padding[CSS_LEFT]); } else { print_number_0("paddingLeft", node->style.padding[CSS_LEFT]); print_number_0("paddingRight", node->style.padding[CSS_RIGHT]); @@ -222,6 +240,10 @@ static void print_css_node_rec( print_number_nan("width", node->style.dimensions[CSS_WIDTH]); print_number_nan("height", node->style.dimensions[CSS_HEIGHT]); + print_number_nan("maxWidth", node->style.maxDimensions[CSS_WIDTH]); + print_number_nan("maxHeight", node->style.maxDimensions[CSS_HEIGHT]); + print_number_nan("minWidth", node->style.minDimensions[CSS_WIDTH]); + print_number_nan("minHeight", node->style.minDimensions[CSS_HEIGHT]); if (node->style.position_type == CSS_POSITION_ABSOLUTE) { printf("position: 'absolute', "); @@ -245,11 +267,10 @@ static void print_css_node_rec( } } -void print_css_node(css_node_t *node, css_print_options_t options) { +void print_css_node(css_node_t* node, css_print_options_t options) { print_css_node_rec(node, options, 0); } - static css_position_t leading[4] = { /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP, /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM, @@ -285,7 +306,41 @@ static bool isColumnDirection(css_flex_direction_t flex_direction) { flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE; } -static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { +static bool isFlexBasisAuto(css_node_t* node) { +#if POSITIVE_FLEX_IS_AUTO + // All flex values are auto. + (void) node; + return true; +#else + // A flex value > 0 implies a basis of zero. + return node->style.flex <= 0; +#endif +} + +static float getFlexGrowFactor(css_node_t* node) { + // Flex grow is implied by positive values for flex. + if (node->style.flex > 0) { + return node->style.flex; + } + return 0; +} + +static float getFlexShrinkFactor(css_node_t* node) { +#if POSITIVE_FLEX_IS_AUTO + // A flex shrink factor of 1 is implied by non-zero values for flex. + if (node->style.flex != 0) { + return 1; + } +#else + // A flex shrink factor of 1 is implied by negative values for flex. + if (node->style.flex < 0) { + return 1; + } +#endif + return 0; +} + +static float getLeadingMargin(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_START])) { return node->style.margin[CSS_START]; } @@ -293,7 +348,7 @@ static float getLeadingMargin(css_node_t *node, css_flex_direction_t axis) { return node->style.margin[leading[axis]]; } -static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingMargin(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.margin[CSS_END])) { return node->style.margin[CSS_END]; } @@ -301,7 +356,7 @@ static float getTrailingMargin(css_node_t *node, css_flex_direction_t axis) { return node->style.margin[trailing[axis]]; } -static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { +static float getLeadingPadding(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.padding[CSS_START]) && node->style.padding[CSS_START] >= 0) { @@ -315,7 +370,7 @@ static float getLeadingPadding(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingPadding(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.padding[CSS_END]) && node->style.padding[CSS_END] >= 0) { @@ -329,7 +384,7 @@ static float getTrailingPadding(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { +static float getLeadingBorder(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.border[CSS_START]) && node->style.border[CSS_START] >= 0) { @@ -343,7 +398,7 @@ static float getLeadingBorder(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingBorder(css_node_t* node, css_flex_direction_t axis) { if (isRowDirection(axis) && !isUndefined(node->style.border[CSS_END]) && node->style.border[CSS_END] >= 0) { @@ -357,34 +412,30 @@ static float getTrailingBorder(css_node_t *node, css_flex_direction_t axis) { return 0; } -static float getLeadingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { +static float getLeadingPaddingAndBorder(css_node_t* node, css_flex_direction_t axis) { return getLeadingPadding(node, axis) + getLeadingBorder(node, axis); } -static float getTrailingPaddingAndBorder(css_node_t *node, css_flex_direction_t axis) { +static float getTrailingPaddingAndBorder(css_node_t* node, css_flex_direction_t axis) { return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); } -static float getBorderAxis(css_node_t *node, css_flex_direction_t axis) { - return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); -} - -static float getMarginAxis(css_node_t *node, css_flex_direction_t axis) { +static float getMarginAxis(css_node_t* node, css_flex_direction_t axis) { return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } -static float getPaddingAndBorderAxis(css_node_t *node, css_flex_direction_t axis) { +static float getPaddingAndBorderAxis(css_node_t* node, css_flex_direction_t axis) { return getLeadingPaddingAndBorder(node, axis) + getTrailingPaddingAndBorder(node, axis); } -static css_align_t getAlignItem(css_node_t *node, css_node_t *child) { +static css_align_t getAlignItem(css_node_t* node, css_node_t* child) { if (child->style.align_self != CSS_ALIGN_AUTO) { return child->style.align_self; } return node->style.align_items; } -static css_direction_t resolveDirection(css_node_t *node, css_direction_t parentDirection) { +static css_direction_t resolveDirection(css_node_t* node, css_direction_t parentDirection) { css_direction_t direction = node->style.direction; if (direction == CSS_DIRECTION_INHERIT) { @@ -394,7 +445,7 @@ static css_direction_t resolveDirection(css_node_t *node, css_direction_t parent return direction; } -static css_flex_direction_t getFlexDirection(css_node_t *node) { +static css_flex_direction_t getFlexDirection(css_node_t* node) { return node->style.flex_direction; } @@ -418,46 +469,46 @@ static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_dire } } -static float getFlex(css_node_t *node) { +static float getFlex(css_node_t* node) { return node->style.flex; } -static bool isFlex(css_node_t *node) { +static bool isFlex(css_node_t* node) { return ( node->style.position_type == CSS_POSITION_RELATIVE && - getFlex(node) > 0 + getFlex(node) != 0 ); } -static bool isFlexWrap(css_node_t *node) { +static bool isFlexWrap(css_node_t* node) { return node->style.flex_wrap == CSS_WRAP; } -static float getDimWithMargin(css_node_t *node, css_flex_direction_t axis) { - return node->layout.dimensions[dim[axis]] + +static float getDimWithMargin(css_node_t* node, css_flex_direction_t axis) { + return node->layout.measured_dimensions[dim[axis]] + getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } -static bool isStyleDimDefined(css_node_t *node, css_flex_direction_t axis) { +static bool isStyleDimDefined(css_node_t* node, css_flex_direction_t axis) { float value = node->style.dimensions[dim[axis]]; return !isUndefined(value) && value >= 0.0; } -static bool isLayoutDimDefined(css_node_t *node, css_flex_direction_t axis) { - float value = node->layout.dimensions[dim[axis]]; +static bool isLayoutDimDefined(css_node_t* node, css_flex_direction_t axis) { + float value = node->layout.measured_dimensions[dim[axis]]; return !isUndefined(value) && value >= 0.0; } -static bool isPosDefined(css_node_t *node, css_position_t position) { +static bool isPosDefined(css_node_t* node, css_position_t position) { return !isUndefined(node->style.position[position]); } -static bool isMeasureDefined(css_node_t *node) { +static bool isMeasureDefined(css_node_t* node) { return node->measure; } -static float getPosition(css_node_t *node, css_position_t position) { +static float getPosition(css_node_t* node, css_position_t position) { float result = node->style.position[position]; if (!isUndefined(result)) { return result; @@ -465,7 +516,7 @@ static float getPosition(css_node_t *node, css_position_t position) { return 0; } -static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) { +static float boundAxisWithinMinAndMax(css_node_t* node, css_flex_direction_t axis, float value) { float min = CSS_UNDEFINED; float max = CSS_UNDEFINED; @@ -489,32 +540,22 @@ static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) return boundValue; } -// When the user specifically sets a value for width or height -static void setDimensionFromStyle(css_node_t *node, css_flex_direction_t axis) { - // The parent already computed us a width or height. We just skip it - if (isLayoutDimDefined(node, axis)) { - return; - } - // We only run if there's a width or height defined - if (!isStyleDimDefined(node, axis)) { - return; - } - - // The dimensions can never be smaller than the padding and border - node->layout.dimensions[dim[axis]] = fmaxf( - boundAxis(node, axis, node->style.dimensions[dim[axis]]), - getPaddingAndBorderAxis(node, axis) - ); +// Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the +// padding and border amount. +static float boundAxis(css_node_t* node, css_flex_direction_t axis, float value) { + return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis)); } -static void setTrailingPosition(css_node_t *node, css_node_t *child, css_flex_direction_t axis) { - child->layout.position[trailing[axis]] = node->layout.dimensions[dim[axis]] - - child->layout.dimensions[dim[axis]] - child->layout.position[pos[axis]]; - } +static void setTrailingPosition(css_node_t* node, css_node_t* child, css_flex_direction_t axis) { + float size = child->style.position_type == CSS_POSITION_ABSOLUTE ? + 0 : + child->layout.measured_dimensions[dim[axis]]; + child->layout.position[trailing[axis]] = node->layout.measured_dimensions[dim[axis]] - size - child->layout.position[pos[axis]]; +} // If both left and right are defined, then use left. Otherwise return // +left or -right depending on which is defined. -static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { +static float getRelativePosition(css_node_t* node, css_flex_direction_t axis) { float lead = node->style.position[leading[axis]]; if (!isUndefined(lead)) { return lead; @@ -522,352 +563,388 @@ static float getRelativePosition(css_node_t *node, css_flex_direction_t axis) { return -getPosition(node, trailing[axis]); } -static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) { - /** START_GENERATED **/ - css_direction_t direction = resolveDirection(node, parentDirection); +static void setPosition(css_node_t* node, css_direction_t direction) { css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); - css_flex_direction_t resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); + + node->layout.position[leading[mainAxis]] = getLeadingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node->layout.position[leading[crossAxis]] = getLeadingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + node->layout.position[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); +} - // Handle width and height style attributes - setDimensionFromStyle(node, mainAxis); - setDimensionFromStyle(node, crossAxis); +// +// This is the main routine that implements a subset of the flexbox layout algorithm +// described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/. +// +// Limitations of this algorithm, compared to the full standard: +// * Display property is always assumed to be 'flex' except for Text nodes, which +// are assumed to be 'inline-flex'. +// * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are +// stacked in document order. +// * The 'order' property is not supported. The order of flex items is always defined +// by document order. +// * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse' +// and 'hidden' are not supported. +// * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The +// rarely-used 'wrap-reverse' is not supported. +// * Rather than allowing arbitrary combinations of flexGrow, flexShrink and +// flexBasis, this algorithm supports only the three most common combinations: +// flex: 0 is equiavlent to flex: 0 0 auto +// flex: n (where n is a positive value) is equivalent to flex: n 1 auto +// If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0 +// This is faster because the content doesn't need to be measured, but it's +// less flexible because the basis is always 0 and can't be overriden with +// the width/height attributes. +// flex: -1 (or any negative value) is equivalent to flex: 0 1 auto +// * Margins cannot be specified as 'auto'. They must be specified in terms of pixel +// values, and the default value is 0. +// * The 'baseline' value is not supported for alignItems and alignSelf properties. +// * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be +// specified as pixel values, not as percentages. +// * There is no support for calculation of dimensions based on intrinsic aspect ratios +// (e.g. images). +// * There is no support for forced breaks. +// * It does not support vertical inline directions (top-to-bottom or bottom-to-top text). +// +// Deviations from standard: +// * Section 4.5 of the spec indicates that all flex items have a default minimum +// main size. For text blocks, for example, this is the width of the widest word. +// Calculating the minimum width is expensive, so we forego it and assume a default +// minimum main size of 0. +// * Min/Max sizes in the main axis are not honored when resolving flexible lengths. +// * The spec indicates that the default value for 'flexDirection' is 'row', but +// the algorithm below assumes a default of 'column'. +// +// Input parameters: +// - node: current node to be sized and layed out +// - availableWidth & availableHeight: available size to be used for sizing the node +// or CSS_UNDEFINED if the size is not available; interpretation depends on layout +// flags +// - parentDirection: the inline (text) direction within the parent (left-to-right or +// right-to-left) +// - widthMeasureMode: indicates the sizing rules for the width (see below for explanation) +// - heightMeasureMode: indicates the sizing rules for the height (see below for explanation) +// - performLayout: specifies whether the caller is interested in just the dimensions +// of the node or it requires the entire node and its subtree to be layed out +// (with final positions) +// +// Details: +// This routine is called recursively to lay out subtrees of flexbox elements. It uses the +// information in node.style, which is treated as a read-only input. It is responsible for +// setting the layout.direction and layout.measured_dimensions fields for the input node as well +// as the layout.position and layout.line_index fields for its child nodes. The +// layout.measured_dimensions field includes any border or padding for the node but does +// not include margins. +// +// The spec describes four different layout modes: "fill available", "max content", "min content", +// and "fit content". Of these, we don't use "min content" because we don't support default +// minimum main sizes (see above for details). Each of our measure modes maps to a layout mode +// from the spec (https://www.w3.org/TR/css3-sizing/#terms): +// - CSS_MEASURE_MODE_UNDEFINED: max content +// - CSS_MEASURE_MODE_EXACTLY: fill available +// - CSS_MEASURE_MODE_AT_MOST: fit content +// +// When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of +// undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension. +// +static void layoutNodeImpl(css_node_t* node, float availableWidth, float availableHeight, + css_direction_t parentDirection, css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, bool performLayout) { + /** START_GENERATED **/ - // Set the resolved resolution in the node's layout + assert(isUndefined(availableWidth) ? widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED : true); // availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED + assert(isUndefined(availableHeight) ? heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED : true); // availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED + + float paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); + float marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + float marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + + // Set the resolved resolution in the node's layout. + css_direction_t direction = resolveDirection(node, parentDirection); node->layout.direction = direction; - // The position is set by the parent, but we need to complete it with a - // delta composed of the margin and left/top/right/bottom - node->layout.position[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node->layout.position[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node->layout.position[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - node->layout.position[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - - // Inline immutable values from the target node to avoid excessive method - // invocations during the layout calculation. - int childCount = node->children_count; - float paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis); - float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - + // For content (text) nodes, determine the dimensions based on the text contents. if (isMeasureDefined(node)) { - bool isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis); + float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + + if (widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && heightMeasureMode == CSS_MEASURE_MODE_EXACTLY) { - float width = CSS_UNDEFINED; - css_measure_mode_t widthMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, resolvedRowAxis)) { - width = node->style.dimensions[CSS_WIDTH]; - widthMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isResolvedRowDimDefined) { - width = node->layout.dimensions[dim[resolvedRowAxis]]; - widthMode = CSS_MEASURE_MODE_EXACTLY; + // Don't bother sizing the text if both dimensions are already defined. + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); + } else if (innerWidth <= 0) { + + // Don't bother sizing the text if there's no horizontal space. + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { - width = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis); - widthMode = CSS_MEASURE_MODE_AT_MOST; - } - width -= paddingAndBorderAxisResolvedRow; - if (isUndefined(width)) { - widthMode = CSS_MEASURE_MODE_UNDEFINED; - } - float height = CSS_UNDEFINED; - css_measure_mode_t heightMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node->style.dimensions[CSS_HEIGHT]; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else { - height = parentMaxHeight - - getMarginAxis(node, resolvedRowAxis); - heightMode = CSS_MEASURE_MODE_AT_MOST; - } - height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - if (isUndefined(height)) { - heightMode = CSS_MEASURE_MODE_UNDEFINED; - } - - // We only need to give a dimension for the text if we haven't got any - // for it computed yet. It can either be from the style attribute or because - // the element is flexible. - bool isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined; - bool isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && - isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); - - // Let's not measure the text if we already know both dimensions - if (isRowUndefined || isColumnUndefined) { + // Measure the text under the current constraints. css_dim_t measureDim = node->measure( node->context, - width, - widthMode, - height, - heightMode + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode ); - if (isRowUndefined) { - node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] + - paddingAndBorderAxisResolvedRow; - } - if (isColumnUndefined) { - node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] + - paddingAndBorderAxisColumn; - } + + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + measureDim.dimensions[CSS_WIDTH] + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + measureDim.dimensions[CSS_HEIGHT] + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); } - if (childCount == 0) { + + return; + } + + // For nodes with no children, use the available values if they were provided, or + // the minimum size as indicated by the padding and border sizes. + int childCount = node->children_count; + if (childCount == 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode == CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); + return; + } + + // If we're not being asked to perform a full layout, we can handle a number of common + // cases here without incurring the cost of the remaining function. + if (!performLayout) { + // If we're being asked to size the content with an at most constraint but there is no available width, + // the measurement will always be zero. + if (widthMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 && + heightMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + if (widthMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn)); + return; + } + + if (heightMeasureMode == CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow)); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + // If we're being asked to use an exact width/height, there's no need to measure the children. + if (widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && heightMeasureMode == CSS_MEASURE_MODE_EXACTLY) { + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); return; } } - bool isNodeFlexWrap = isFlexWrap(node); - + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + css_flex_direction_t mainAxis = resolveAxis(getFlexDirection(node), direction); + css_flex_direction_t crossAxis = getCrossFlexDirection(mainAxis, direction); + bool isMainAxisRow = isRowDirection(mainAxis); css_justify_t justifyContent = node->style.justify_content; - - float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); - float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); - float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); - float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); - - bool isMainDimDefined = isLayoutDimDefined(node, mainAxis); - bool isCrossDimDefined = isLayoutDimDefined(node, crossAxis); - bool isMainRowDirection = isRowDirection(mainAxis); - - int i; - int ii; - css_node_t* child; - css_flex_direction_t axis; + bool isNodeFlexWrap = isFlexWrap(node); css_node_t* firstAbsoluteChild = NULL; css_node_t* currentAbsoluteChild = NULL; - float definedMainDim = CSS_UNDEFINED; - if (isMainDimDefined) { - definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; + float leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); + float trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis); + float leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); + float paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); + float paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); + + css_measure_mode_t measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + css_measure_mode_t measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; + float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + css_node_t* child; + int i; + float childWidth; + float childHeight; + css_measure_mode_t childWidthMeasureMode; + css_measure_mode_t childHeightMeasureMode; + for (i = 0; i < childCount; i++) { + child = node->get_child(node->context, i); + + if (performLayout) { + // Set the initial position (relative to the parent). + css_direction_t childDirection = resolveDirection(child, direction); + setPosition(child, childDirection); + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if (child->style.position_type == CSS_POSITION_ABSOLUTE) { + + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild == NULL) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild != NULL) { + currentAbsoluteChild->next_child = child; + } + currentAbsoluteChild = child; + child->next_child = NULL; + } else { + + if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + + // The width is definite, so use that as the flex basis. + child->layout.flex_basis = fmaxf(child->style.dimensions[CSS_WIDTH], getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW)); + } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + + // The height is definite, so use that as the flex basis. + child->layout.flex_basis = fmaxf(child->style.dimensions[CSS_HEIGHT], getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN)); + } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) { + + // If the basis isn't 'auto', it is assumed to be zero. + child->layout.flex_basis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis)); + } else { + + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + childWidth = child->style.dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = child->style.dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + // Measure the child + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "measure"); + + child->layout.flex_basis = fmaxf(isMainAxisRow ? child->layout.measured_dimensions[CSS_WIDTH] : child->layout.measured_dimensions[CSS_HEIGHT], getPaddingAndBorderAxis(child, mainAxis)); + } + } } - // We want to execute the next two loops one per line with flex-wrap - int startLine = 0; - int endLine = 0; - // int nextOffset = 0; - int alreadyComputedNextLayout = 0; - // We aggregate the total dimensions of the container in those two variables - float linesCrossDim = 0; - float linesMainDim = 0; - int linesCount = 0; - while (endLine < childCount) { - // Layout non flexible children and count children by type + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + int startOfLineIndex = 0; + int endOfLineIndex = 0; + + // Number of lines. + int lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; - // mainContentDim is accumulation of the dimensions and margin of all the - // non flexible children. This will be used in order to either set the - // dimensions of the node if none already exist, or to compute the - // remaining space left for the flexible children. - float mainContentDim = 0; + // Max main dimension of all the lines. + float maxLineMainDim = 0; - // There are three kind of children, non flexible, flexible and absolute. - // We need to know how many there are in order to distribute the space. - int flexibleChildrenCount = 0; - float totalFlexible = 0; - int nonFlexibleChildrenCount = 0; + while (endOfLineIndex < childCount) { + + // Number of items on the currently line. May be different than the difference + // between start and end indicates because we skip over absolute-positioned items. + int itemsOnLine = 0; - // Use the line loop to position children in the main axis for as long - // as they are using a simple stacking behaviour. Children that are - // immediately stacked in the initial loop will not be touched again - // in . - bool isSimpleStackMain = - (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) || - (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER); - int firstComplexMain = (isSimpleStackMain ? childCount : startLine); + // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin + // of all the children on the current line. This will be used in order to + // either set the dimensions of the node if none already exist or to compute + // the remaining space left for the flexible children. + float sizeConsumedOnCurrentLine = 0; - // Use the initial line loop to position children in the cross axis for - // as long as they are relatively positioned with alignment STRETCH or - // FLEX_START. Children that are immediately stacked in the initial loop - // will not be touched again in . - bool isSimpleStackCross = true; - int firstComplexCross = childCount; + float totalFlexGrowFactors = 0; + float totalFlexShrinkScaledFactors = 0; - css_node_t* firstFlexChild = NULL; - css_node_t* currentFlexChild = NULL; + i = startOfLineIndex; - float mainDim = leadingPaddingAndBorderMain; - float crossDim = 0; + // Maintain a linked list of the child nodes that can shrink and/or grow. + css_node_t* firstRelativeChild = NULL; + css_node_t* currentRelativeChild = NULL; - float maxWidth = CSS_UNDEFINED; - float maxHeight = CSS_UNDEFINED; - for (i = startLine; i < childCount; ++i) { + // Add items to the current line until it's full or we run out of items. + while (i < childCount) { child = node->get_child(node->context, i); - child->line_index = linesCount; + child->line_index = lineCount; - child->next_absolute_child = NULL; - child->next_flex_child = NULL; - - css_align_t alignItem = getAlignItem(node, child); - - // Pre-fill cross axis dimensions when the child is using stretch before - // we call the recursive layout pass - if (alignItem == CSS_ALIGN_STRETCH && - child->style.position_type == CSS_POSITION_RELATIVE && - isCrossDimDefined && - !isStyleDimDefined(child, crossAxis)) { - child->layout.dimensions[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) { - // Store a private linked list of absolutely positioned children - // so that we can efficiently traverse them later. - if (firstAbsoluteChild == NULL) { - firstAbsoluteChild = child; + if (child->style.position_type != CSS_POSITION_ABSOLUTE) { + float outerFlexBasis = child->layout.flex_basis + getMarginAxis(child, mainAxis); + + // If this is a multi-line flow and this item pushes us over the available size, we've + // hit the end of the current line. Break out of the loop and lay out the current line. + if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) { + break; } - if (currentAbsoluteChild != NULL) { - currentAbsoluteChild->next_absolute_child = child; - } - currentAbsoluteChild = child; - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(child, axis) && - isPosDefined(child, leading[axis]) && - isPosDefined(child, trailing[axis])) { - child->layout.dimensions[dim[axis]] = fmaxf( - boundAxis(child, axis, node->layout.dimensions[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis])), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + sizeConsumedOnCurrentLine += outerFlexBasis; + itemsOnLine++; + + if (isFlex(child)) { + totalFlexGrowFactors += getFlexGrowFactor(child); + + // Unlike the grow factor, the shrink factor is scaled relative to the child + // dimension. + totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child->layout.flex_basis; } + + // Store a private linked list of children that need to be layed out. + if (firstRelativeChild == NULL) { + firstRelativeChild = child; + } + if (currentRelativeChild != NULL) { + currentRelativeChild->next_child = child; + } + currentRelativeChild = child; + child->next_child = NULL; } - - float nextContentDim = 0; - - // It only makes sense to consider a child flexible if we have a computed - // dimension for the node-> - if (isMainDimDefined && isFlex(child)) { - flexibleChildrenCount++; - totalFlexible += child->style.flex; - - // Store a private linked list of flexible children so that we can - // efficiently traverse them later. - if (firstFlexChild == NULL) { - firstFlexChild = child; - } - if (currentFlexChild != NULL) { - currentFlexChild->next_flex_child = child; - } - currentFlexChild = child; - - // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information, which represents - // the smallest possible size for the child, to compute the remaining - // available space. - nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + - getMarginAxis(child, mainAxis); - - } else { - maxWidth = CSS_UNDEFINED; - maxHeight = CSS_UNDEFINED; - - if (!isMainRowDirection) { - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - } else { - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - } - - // This is the main recursive call. We layout non flexible children. - if (alreadyComputedNextLayout == 0) { - layoutNode(child, maxWidth, maxHeight, direction); - } - - // Absolute positioned elements do not take part of the layout, so we - // don't use them to compute mainContentDim - if (child->style.position_type == CSS_POSITION_RELATIVE) { - nonFlexibleChildrenCount++; - // At this point we know the final size and margin of the element. - nextContentDim = getDimWithMargin(child, mainAxis); - } - } - - // The element we are about to add would make us go to the next line - if (isNodeFlexWrap && - isMainDimDefined && - mainContentDim + nextContentDim > definedMainDim && - // If there's only one element, then it's bigger than the content - // and needs its own line - i != startLine) { - nonFlexibleChildrenCount--; - alreadyComputedNextLayout = 1; - break; - } - - // Disable simple stacking in the main axis for the current line as - // we found a non-trivial child-> The remaining children will be laid out - // in . - if (isSimpleStackMain && - (child->style.position_type != CSS_POSITION_RELATIVE || isFlex(child))) { - isSimpleStackMain = false; - firstComplexMain = i; - } - - // Disable simple stacking in the cross axis for the current line as - // we found a non-trivial child-> The remaining children will be laid out - // in . - if (isSimpleStackCross && - (child->style.position_type != CSS_POSITION_RELATIVE || - (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) || - (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) { - isSimpleStackCross = false; - firstComplexCross = i; - } - - if (isSimpleStackMain) { - child->layout.position[pos[mainAxis]] += mainDim; - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); - } - - mainDim += getDimWithMargin(child, mainAxis); - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); - } - - if (isSimpleStackCross) { - child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); - } - } - - alreadyComputedNextLayout = 0; - mainContentDim += nextContentDim; - endLine = i + 1; + + i++; + endOfLineIndex++; } - - // Layout flexible children and allocate empty space + + // If we don't need to measure the cross axis, we can skip the entire flex step. + bool canSkipFlex = !performLayout && measureModeCrossDim == CSS_MEASURE_MODE_EXACTLY; // In order to position the elements in the main axis, we have two // controls. The space between the beginning and the first element @@ -875,212 +952,300 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM float leadingMainDim = 0; float betweenMainDim = 0; - // The remaining available space that needs to be allocated - float remainingMainDim = 0; - if (isMainDimDefined) { - remainingMainDim = definedMainDim - mainContentDim; - } else { - remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + float remainingFreeSpace = 0; + if (!isUndefined(availableInnerMainDim)) { + remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; + } else if (sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized based on its content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine; + } + + float remainingFreeSpaceAfterFlex = remainingFreeSpace; + + if (!canSkipFlex) { + float childFlexBasis; + float flexShrinkScaledFactor; + float flexGrowFactor; + float baseMainSize; + float boundMainSize; + + // Do two passes over the flex items to figure out how to distribute the remaining space. + // The first pass finds the items whose min/max constraints trigger, freezes them at those + // sizes, and excludes those sizes from the remaining space. The second pass sets the size + // of each flexible item. It distributes the remaining space amongst the items whose min/max + // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing + // their min/max constraints to trigger again. + // + // This two pass approach for resolving min/max constraints deviates from the spec. The + // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process + // that needs to be repeated a variable number of times. The algorithm implemented here + // won't handle all cases but it was simpler to implement and it mitigates performance + // concerns because we know exactly how many passes it'll do. + + // First pass: detect the flex items whose min/max constraints trigger + float deltaFreeSpace = 0; + float deltaFlexShrinkScaledFactors = 0; + float deltaFlexGrowFactors = 0; + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != NULL) { + childFlexBasis = currentRelativeChild->layout.flex_basis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; + } + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexGrowFactors -= flexGrowFactor; + } + } + } + + currentRelativeChild = currentRelativeChild->next_child; + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; + totalFlexGrowFactors += deltaFlexGrowFactors; + remainingFreeSpace += deltaFreeSpace; + remainingFreeSpaceAfterFlex = remainingFreeSpace; + + // Second pass: resolve the sizes of the flexible items + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != NULL) { + childFlexBasis = currentRelativeChild->layout.flex_basis; + float updatedMainSize = childFlexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor); + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor); + } + } + + remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + + if (isMainAxisRow) { + childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childHeight = currentRelativeChild->style.dimensions[CSS_HEIGHT] + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } else { + childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childWidth = currentRelativeChild->style.dimensions[CSS_WIDTH] + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } + + bool requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) && + getAlignItem(node, currentRelativeChild) == CSS_ALIGN_STRETCH; + + // Recursively call the layout algorithm for this child with the updated main size. + layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, "flex"); + + currentRelativeChild = currentRelativeChild->next_child; + } + } + + remainingFreeSpace = remainingFreeSpaceAfterFlex; + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main axis. + // Their dimensions are also set in the cross axis with the exception of items + // that are aligned "stretch". We need to compute these stretch values and + // set the final positions. + + // If we are using "at most" rules in the main axis, we won't distribute + // any remaining space at this point. + if (measureModeMainDim == CSS_MEASURE_MODE_AT_MOST) { + remainingFreeSpace = 0; } - // If there are flexible children in the mix, they are going to fill the - // remaining space - if (flexibleChildrenCount != 0) { - float flexibleMainDim = remainingMainDim / totalFlexible; - float baseMainDim; - float boundMainDim; - - // If the flex share of remaining space doesn't meet min/max bounds, - // remove this child from flex calculations. - currentFlexChild = firstFlexChild; - while (currentFlexChild != NULL) { - baseMainDim = flexibleMainDim * currentFlexChild->style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis); - boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); - - if (baseMainDim != boundMainDim) { - remainingMainDim -= boundMainDim; - totalFlexible -= currentFlexChild->style.flex; - } - - currentFlexChild = currentFlexChild->next_flex_child; - } - flexibleMainDim = remainingMainDim / totalFlexible; - - // The non flexible children can overflow the container, in this case - // we should just assume that there is no space available. - if (flexibleMainDim < 0) { - flexibleMainDim = 0; - } - - currentFlexChild = firstFlexChild; - while (currentFlexChild != NULL) { - // At this point we know the final size of the element in the main - // dimension - currentFlexChild->layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, - flexibleMainDim * currentFlexChild->style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis) - ); - - maxWidth = CSS_UNDEFINED; - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - maxHeight = CSS_UNDEFINED; - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else if (isMainRowDirection) { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - - // And we recursively call the layout algorithm for this child - layoutNode(currentFlexChild, maxWidth, maxHeight, direction); - - child = currentFlexChild; - currentFlexChild = currentFlexChild->next_flex_child; - child->next_flex_child = NULL; - } - - // We use justifyContent to figure out how to allocate the remaining - // space available - } else if (justifyContent != CSS_JUSTIFY_FLEX_START) { + // Use justifyContent to figure out how to allocate the remaining space + // available in the main axis. + if (justifyContent != CSS_JUSTIFY_FLEX_START) { if (justifyContent == CSS_JUSTIFY_CENTER) { - leadingMainDim = remainingMainDim / 2; + leadingMainDim = remainingFreeSpace / 2; } else if (justifyContent == CSS_JUSTIFY_FLEX_END) { - leadingMainDim = remainingMainDim; + leadingMainDim = remainingFreeSpace; } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) { - remainingMainDim = fmaxf(remainingMainDim, 0); - if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) { - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + remainingFreeSpace = fmaxf(remainingFreeSpace, 0); + if (itemsOnLine > 1) { + betweenMainDim = remainingFreeSpace / (itemsOnLine - 1); } else { betweenMainDim = 0; } } else if (justifyContent == CSS_JUSTIFY_SPACE_AROUND) { // Space on the edges is half of the space between elements - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount); + betweenMainDim = remainingFreeSpace / itemsOnLine; leadingMainDim = betweenMainDim / 2; } } - // Position elements in the main axis and compute dimensions + float mainDim = leadingPaddingAndBorderMain + leadingMainDim; + float crossDim = 0; - // At this point, all the children have their dimensions set. We need to - // find their position. In order to do that, we accumulate data in - // variables that are also useful to compute the total dimensions of the - // container! - mainDim += leadingMainDim; - - for (i = firstComplexMain; i < endLine; ++i) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { child = node->get_child(node->context, i); if (child->style.position_type == CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { - // In case the child is position absolute and has left/top being - // defined, we override the position to whatever the user said - // (and margin/border). - child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + - getLeadingBorder(node, mainAxis) + - getLeadingMargin(child, mainAxis); - } else { - // If the child is position absolute (without top/left) or relative, - // we put it at the current accumulated offset. - child->layout.position[pos[mainAxis]] += mainDim; - - // Define the trailing position accordingly. - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child->layout.position[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + + getLeadingBorder(node, mainAxis) + + getLeadingMargin(child, mainAxis); } - - // Now that we placed the element, we need to update the variables - // We only need to do that for relative elements. Absolute elements + } else { + if (performLayout) { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child->layout.position[pos[mainAxis]] += mainDim; + } + + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements // do not take part in that phase. if (child->style.position_type == CSS_POSITION_RELATIVE) { - // The main dimension is the sum of all the elements dimension plus - // the spacing. - mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); - // The cross dimension is the max of the elements dimension since there - // can only be one element in that cross dimension. - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims because + // they weren't computed. This means we can't call getDimWithMargin. + mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child->layout.flex_basis; + crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); + + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis)); + } } } } - float containerCrossAxis = node->layout.dimensions[dim[crossAxis]]; - if (!isCrossDimDefined) { - containerCrossAxis = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); + mainDim += trailingPaddingAndBorderMain; + + float containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim == CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim == CSS_MEASURE_MODE_AT_MOST) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; + + if (measureModeCrossDim == CSS_MEASURE_MODE_AT_MOST) { + containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim); + } } - // Position elements in the cross axis - for (i = firstComplexCross; i < endLine; ++i) { - child = node->get_child(node->context, i); + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim == CSS_MEASURE_MODE_EXACTLY) { + crossDim = availableInnerCrossDim; + } - if (child->style.position_type == CSS_POSITION_ABSOLUTE && - isPosDefined(child, leading[crossAxis])) { - // In case the child is absolutely positionned and has a - // top/left/bottom/right being set, we override all the previously - // computed positions to set it correctly. - child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + - getLeadingBorder(node, crossAxis) + - getLeadingMargin(child, crossAxis); + // Clamp to the min/max size specified on the container. + crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; - } else { - float leadingCrossDim = leadingPaddingAndBorderCross; + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { + child = node->get_child(node->context, i); - // For a relative children, we're either using alignItems (parent) or - // alignSelf (child) in order to determine the position in the cross axis - if (child->style.position_type == CSS_POSITION_RELATIVE) { - /*eslint-disable */ - // This variable is intentionally re-defined as the code is transpiled to a block scope language + if (child->style.position_type == CSS_POSITION_ABSOLUTE) { + // If the child is absolutely positioned and has a top/left/bottom/right + // set, override all the previously computed positions to set it correctly. + if (isPosDefined(child, leading[crossAxis])) { + child->layout.position[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + + getLeadingBorder(node, crossAxis) + + getLeadingMargin(child, crossAxis); + } else { + child->layout.position[pos[crossAxis]] = leadingPaddingAndBorderCross + + getLeadingMargin(child, crossAxis); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis css_align_t alignItem = getAlignItem(node, child); - /*eslint-enable */ + + // If the child uses align stretch, we need to lay it out one more time, this time + // forcing the cross-axis size to be the computed cross size for the current line. if (alignItem == CSS_ALIGN_STRETCH) { - // You can only stretch if the dimension has not already been defined - // previously. - if (!isStyleDimDefined(child, crossAxis)) { - float dimCrossAxis = child->layout.dimensions[dim[crossAxis]]; - child->layout.dimensions[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, containerCrossAxis - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - - // If the size has changed, and this child has children we need to re-layout this child - if (dimCrossAxis != child->layout.dimensions[dim[crossAxis]] && child->children_count > 0) { - // Reset child margins before re-layout as they are added back in layoutNode and would be doubled - child->layout.position[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child->layout.position[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child->layout.position[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - child->layout.position[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - - layoutNode(child, maxWidth, maxHeight, direction); - } + childWidth = child->layout.measured_dimensions[CSS_WIDTH] + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childHeight = child->layout.measured_dimensions[CSS_HEIGHT] + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + bool isCrossSizeDefinite = false; + + if (isMainAxisRow) { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN); + childHeight = crossDim; + } else { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW); + childWidth = crossDim; + } + + // If the child defines a definite size for its cross axis, there's no need to stretch. + if (!isCrossSizeDefinite) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, "stretch"); } } else if (alignItem != CSS_ALIGN_FLEX_START) { - // The remaining space between the parent dimensions+padding and child - // dimensions+margin. - float remainingCrossDim = containerCrossAxis - - paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis); + float remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis); if (alignItem == CSS_ALIGN_CENTER) { leadingCrossDim += remainingCrossDim / 2; @@ -1088,41 +1253,25 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM leadingCrossDim += remainingCrossDim; } } - } - // And we apply the position - child->layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; - - // Define the trailing position accordingly. - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); + // And we apply the position + child->layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; } } } - linesCrossDim += crossDim; - linesMainDim = fmaxf(linesMainDim, mainDim); - linesCount += 1; - startLine = endLine; + totalLineCrossDim += crossDim; + maxLineMainDim = fmaxf(maxLineMainDim, mainDim); + + // Reset variables for new line. + lineCount++; + startOfLineIndex = endOfLineIndex; + endOfLineIndex = startOfLineIndex; } - // - // - // Note(prenaux): More than one line, we need to layout the crossAxis - // according to alignContent. - // - // Note that we could probably remove and handle the one line case - // here too, but for the moment this is safer since it won't interfere with - // previously working code. - // - // See specs: - // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm - // section 9.4 - // - if (linesCount > 1 && isCrossDimDefined) { - float nodeCrossAxisInnerSize = node->layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross; - float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) { + float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; float crossDimLead = 0; float currentLead = leadingPaddingAndBorderCross; @@ -1133,19 +1282,20 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM } else if (alignContent == CSS_ALIGN_CENTER) { currentLead += remainingAlignContentDim / 2; } else if (alignContent == CSS_ALIGN_STRETCH) { - if (nodeCrossAxisInnerSize > linesCrossDim) { - crossDimLead = (remainingAlignContentDim / linesCount); + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = (remainingAlignContentDim / lineCount); } } int endIndex = 0; - for (i = 0; i < linesCount; ++i) { + for (i = 0; i < lineCount; ++i) { int startIndex = endIndex; + int j; // compute the line's height and find the endIndex float lineHeight = 0; - for (ii = startIndex; ii < childCount; ++ii) { - child = node->get_child(node->context, ii); + for (j = startIndex; j < childCount; ++j) { + child = node->get_child(node->context, j); if (child->style.position_type != CSS_POSITION_RELATIVE) { continue; } @@ -1153,33 +1303,33 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM break; } if (isLayoutDimDefined(child, crossAxis)) { - lineHeight = fmaxf( - lineHeight, - child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis) - ); + lineHeight = fmaxf(lineHeight, + child->layout.measured_dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis)); } } - endIndex = ii; + endIndex = j; lineHeight += crossDimLead; - for (ii = startIndex; ii < endIndex; ++ii) { - child = node->get_child(node->context, ii); - if (child->style.position_type != CSS_POSITION_RELATIVE) { - continue; - } + if (performLayout) { + for (j = startIndex; j < endIndex; ++j) { + child = node->get_child(node->context, j); + if (child->style.position_type != CSS_POSITION_RELATIVE) { + continue; + } - css_align_t alignContentAlignItem = getAlignItem(node, child); - if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { - child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { - child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.dimensions[dim[crossAxis]]; - } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { - float childHeight = child->layout.dimensions[dim[crossAxis]]; - child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; - } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { - child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - // TODO(prenaux): Correctly set the height of items with undefined - // (auto) crossAxis dimension. + css_align_t alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSS_ALIGN_FLEX_START) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem == CSS_ALIGN_FLEX_END) { + child->layout.position[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child->layout.measured_dimensions[dim[crossAxis]]; + } else if (alignContentAlignItem == CSS_ALIGN_CENTER) { + childHeight = child->layout.measured_dimensions[dim[crossAxis]]; + child->layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem == CSS_ALIGN_STRETCH) { + child->layout.position[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with indefinite + // (auto) crossAxis dimension. + } } } @@ -1187,137 +1337,343 @@ static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, float parentM } } - bool needsMainTrailingPos = false; - bool needsCrossTrailingPos = false; + // STEP 9: COMPUTING FINAL DIMENSIONS + node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (!isMainDimDefined) { - node->layout.dimensions[dim[mainAxis]] = fmaxf( - // We're missing the last padding at this point to get the final - // dimension - boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), - // We can never assign a width smaller than the padding and borders - paddingAndBorderAxisMain - ); + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim == CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->layout.measured_dimensions[dim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim); + } else if (measureModeMainDim == CSS_MEASURE_MODE_AT_MOST) { + node->layout.measured_dimensions[dim[mainAxis]] = fmaxf( + fminf(availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)), + paddingAndBorderAxisMain); + } + + if (measureModeCrossDim == CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node->layout.measured_dimensions[dim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross); + } else if (measureModeCrossDim == CSS_MEASURE_MODE_AT_MOST) { + node->layout.measured_dimensions[dim[crossAxis]] = fmaxf( + fminf(availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)), + paddingAndBorderAxisCross); + } + + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + if (performLayout) { + bool needsMainTrailingPos = false; + bool needsCrossTrailingPos = false; if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsMainTrailingPos = true; } - } - - if (!isCrossDimDefined) { - node->layout.dimensions[dim[crossAxis]] = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsCrossTrailingPos = true; } - } - // Set trailing position if necessary - if (needsMainTrailingPos || needsCrossTrailingPos) { - for (i = 0; i < childCount; ++i) { - child = node->get_child(node->context, i); + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node->get_child(node->context, i); - if (needsMainTrailingPos) { - setTrailingPosition(node, child, mainAxis); - } + if (needsMainTrailingPos) { + setTrailingPosition(node, child, mainAxis); + } - if (needsCrossTrailingPos) { - setTrailingPosition(node, child, crossAxis); + if (needsCrossTrailingPos) { + setTrailingPosition(node, child, crossAxis); + } } } } - - // Calculate dimensions for absolutely positioned elements + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN currentAbsoluteChild = firstAbsoluteChild; while (currentAbsoluteChild != NULL) { - // Pre-fill dimensions when using absolute position and both offsets for - // the axis are defined (either both left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + // Now that we know the bounds of the container, perform layout again on the + // absolutely-positioned children. + if (performLayout) { - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(currentAbsoluteChild, axis) && - isPosDefined(currentAbsoluteChild, leading[axis]) && - isPosDefined(currentAbsoluteChild, trailing[axis])) { - currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf( - boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] - - getBorderAxis(node, axis) - - getMarginAxis(currentAbsoluteChild, axis) - - getPosition(currentAbsoluteChild, leading[axis]) - - getPosition(currentAbsoluteChild, trailing[axis]) - ), - // You never want to go smaller than padding - getPaddingAndBorderAxis(currentAbsoluteChild, axis) - ); + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = currentAbsoluteChild->style.dimensions[CSS_WIDTH] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + } else { + // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) { + childWidth = node->layout.measured_dimensions[CSS_WIDTH] - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) - + (currentAbsoluteChild->style.position[CSS_LEFT] + currentAbsoluteChild->style.position[CSS_RIGHT]); + childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth); + } + } + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = currentAbsoluteChild->style.dimensions[CSS_HEIGHT] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } else { + // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) { + childHeight = node->layout.measured_dimensions[CSS_HEIGHT] - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) - + (currentAbsoluteChild->style.position[CSS_TOP] + currentAbsoluteChild->style.position[CSS_BOTTOM]); + childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight); + } } - if (isPosDefined(currentAbsoluteChild, trailing[axis]) && - !isPosDefined(currentAbsoluteChild, leading[axis])) { - currentAbsoluteChild->layout.position[leading[axis]] = - node->layout.dimensions[dim[axis]] - - currentAbsoluteChild->layout.dimensions[dim[axis]] - - getPosition(currentAbsoluteChild, trailing[axis]); + // If we're still missing one or the other dimension, measure the content. + if (isUndefined(childWidth) || isUndefined(childHeight)) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node->style.overflow == CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "abs-measure"); + childWidth = currentAbsoluteChild->layout.measured_dimensions[CSS_WIDTH] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + childHeight = currentAbsoluteChild->layout.measured_dimensions[CSS_HEIGHT] + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, "abs-layout"); + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) { + currentAbsoluteChild->layout.position[leading[CSS_FLEX_DIRECTION_ROW]] = + node->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + currentAbsoluteChild->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]); + } + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) { + currentAbsoluteChild->layout.position[leading[CSS_FLEX_DIRECTION_COLUMN]] = + node->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + currentAbsoluteChild->layout.measured_dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]); } } - child = currentAbsoluteChild; - currentAbsoluteChild = currentAbsoluteChild->next_absolute_child; - child->next_absolute_child = NULL; + currentAbsoluteChild = currentAbsoluteChild->next_child; } /** END_GENERATED **/ } -void layoutNode(css_node_t *node, float parentMaxWidth, float parentMaxHeight, css_direction_t parentDirection) { - css_layout_t *layout = &node->layout; - css_direction_t direction = node->style.direction; - layout->should_update = true; +int gDepth = 0; +bool gPrintTree = false; +bool gPrintChanges = false; +bool gPrintSkips = false; - bool skipLayout = - !node->is_dirty(node->context) && - eq(layout->last_requested_dimensions[CSS_WIDTH], layout->dimensions[CSS_WIDTH]) && - eq(layout->last_requested_dimensions[CSS_HEIGHT], layout->dimensions[CSS_HEIGHT]) && - eq(layout->last_parent_max_width, parentMaxWidth) && - eq(layout->last_parent_max_height, parentMaxHeight) && - eq(layout->last_direction, direction); +static const char* spacer = " "; - if (skipLayout) { - layout->dimensions[CSS_WIDTH] = layout->last_dimensions[CSS_WIDTH]; - layout->dimensions[CSS_HEIGHT] = layout->last_dimensions[CSS_HEIGHT]; - layout->position[CSS_TOP] = layout->last_position[CSS_TOP]; - layout->position[CSS_LEFT] = layout->last_position[CSS_LEFT]; +static const char* getSpacer(unsigned long level) { + unsigned long spacerLen = strlen(spacer); + if (level > spacerLen) { + level = spacerLen; + } + return &spacer[spacerLen - level]; +} + +static const char* getModeName(css_measure_mode_t mode, bool performLayout) { + const char* kMeasureModeNames[CSS_MEASURE_MODE_COUNT] = { + "UNDEFINED", + "EXACTLY", + "AT_MOST" + }; + const char* kLayoutModeNames[CSS_MEASURE_MODE_COUNT] = { + "LAY_UNDEFINED", + "LAY_EXACTLY", + "LAY_AT_MOST" + }; + + if (mode >= CSS_MEASURE_MODE_COUNT) { + return ""; + } + + return performLayout? kLayoutModeNames[mode] : kMeasureModeNames[mode]; +} + + +// +// This is a wrapper around the layoutNodeImpl function. It determines +// whether the layout request is redundant and can be skipped. +// +// Parameters: +// Input parameters are the same as layoutNodeImpl (see above) +// Return parameter is true if layout was performed, false if skipped +// +bool layoutNodeInternal(css_node_t* node, float availableWidth, float availableHeight, + css_direction_t parentDirection, css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, bool performLayout, char* reason) { + css_layout_t* layout = &node->layout; + + gDepth++; + + bool needToVisitNode = (node->is_dirty(node->context) && layout->generation_count != gCurrentGenerationCount) || + layout->last_parent_direction != parentDirection; + + if (needToVisitNode) { + // Invalidate the cached results. + layout->next_cached_measurements_index = 0; + layout->cached_layout.width_measure_mode = (css_measure_mode_t)-1; + layout->cached_layout.height_measure_mode = (css_measure_mode_t)-1; + } + + css_cached_measurement_t* cachedResults = NULL; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the positions + // and dimensions for nodes in the subtree. The algorithm assumes that each node + // gets layed out a maximum of one time per tree layout, but multiple measurements + // may be required to resolve all of the flex dimensions. + if (performLayout) { + if (eq(layout->cached_layout.available_width, availableWidth) && + eq(layout->cached_layout.available_height, availableHeight) && + layout->cached_layout.width_measure_mode == widthMeasureMode && + layout->cached_layout.height_measure_mode == heightMeasureMode) { + + cachedResults = &layout->cached_layout; + } } else { - layout->last_requested_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; - layout->last_requested_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; - layout->last_parent_max_width = parentMaxWidth; - layout->last_parent_max_height = parentMaxHeight; - layout->last_direction = direction; + for (int i = 0; i < layout->next_cached_measurements_index; i++) { + if (eq(layout->cached_measurements[i].available_width, availableWidth) && + eq(layout->cached_measurements[i].available_height, availableHeight) && + layout->cached_measurements[i].width_measure_mode == widthMeasureMode && + layout->cached_measurements[i].height_measure_mode == heightMeasureMode) { - for (int i = 0, childCount = node->children_count; i < childCount; i++) { - resetNodeLayout(node->get_child(node->context, i)); + cachedResults = &layout->cached_measurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != NULL) { + layout->measured_dimensions[CSS_WIDTH] = cachedResults->computed_width; + layout->measured_dimensions[CSS_HEIGHT] = cachedResults->computed_height; + + if (gPrintChanges && gPrintSkips) { + printf("%s%d.{[skipped] ", getSpacer(gDepth), gDepth); + if (node->print) { + node->print(node->context); + } + printf("wm: %s, hm: %s, aw: %f ah: %f => d: (%f, %f) %s\n", + getModeName(widthMeasureMode, performLayout), + getModeName(heightMeasureMode, performLayout), + availableWidth, availableHeight, + cachedResults->computed_width, cachedResults->computed_height, reason); + } + } else { + + if (gPrintChanges) { + printf("%s%d.{%s", getSpacer(gDepth), gDepth, needToVisitNode ? "*" : ""); + if (node->print) { + node->print(node->context); + } + printf("wm: %s, hm: %s, aw: %f ah: %f %s\n", + getModeName(widthMeasureMode, performLayout), + getModeName(heightMeasureMode, performLayout), + availableWidth, availableHeight, reason); } - layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection); + layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout); + + if (gPrintChanges) { + printf("%s%d.}%s", getSpacer(gDepth), gDepth, needToVisitNode ? "*" : ""); + if (node->print) { + node->print(node->context); + } + printf("wm: %s, hm: %s, d: (%f, %f) %s\n", + getModeName(widthMeasureMode, performLayout), + getModeName(heightMeasureMode, performLayout), + layout->measured_dimensions[CSS_WIDTH], layout->measured_dimensions[CSS_HEIGHT], reason); + } - layout->last_dimensions[CSS_WIDTH] = layout->dimensions[CSS_WIDTH]; - layout->last_dimensions[CSS_HEIGHT] = layout->dimensions[CSS_HEIGHT]; - layout->last_position[CSS_TOP] = layout->position[CSS_TOP]; - layout->last_position[CSS_LEFT] = layout->position[CSS_LEFT]; + layout->last_parent_direction = parentDirection; + + if (cachedResults == NULL) { + if (layout->next_cached_measurements_index == CSS_MAX_CACHED_RESULT_COUNT) { + if (gPrintChanges) { + printf("Out of cache entries!\n"); + } + layout->next_cached_measurements_index = 0; + } + + css_cached_measurement_t* newCacheEntry; + if (performLayout) { + // Use the single layout cache entry. + newCacheEntry = &layout->cached_layout; + } else { + // Allocate a new measurement cache entry. + newCacheEntry = &layout->cached_measurements[layout->next_cached_measurements_index]; + layout->next_cached_measurements_index++; + } + + newCacheEntry->available_width = availableWidth; + newCacheEntry->available_height = availableHeight; + newCacheEntry->width_measure_mode = widthMeasureMode; + newCacheEntry->height_measure_mode = heightMeasureMode; + newCacheEntry->computed_width = layout->measured_dimensions[CSS_WIDTH]; + newCacheEntry->computed_height = layout->measured_dimensions[CSS_HEIGHT]; + } + } + + if (performLayout) { + node->layout.dimensions[CSS_WIDTH] = node->layout.measured_dimensions[CSS_WIDTH]; + node->layout.dimensions[CSS_HEIGHT] = node->layout.measured_dimensions[CSS_HEIGHT]; + layout->should_update = true; + } + + gDepth--; + layout->generation_count = gCurrentGenerationCount; + return (needToVisitNode || cachedResults == NULL); +} + +void layoutNode(css_node_t* node, float availableWidth, float availableHeight, css_direction_t parentDirection) { + // Increment the generation count. This will force the recursive routine to visit + // all dirty nodes at least once. Subsequent visits will be skipped if the input + // parameters don't change. + gCurrentGenerationCount++; + + // If the caller didn't specify a height/width, use the dimensions + // specified in the style. + if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + availableWidth = node->style.dimensions[CSS_WIDTH] + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + } + if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + availableHeight = node->style.dimensions[CSS_HEIGHT] + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + } + + css_measure_mode_t widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + css_measure_mode_t heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, "initial")) { + + setPosition(node, node->layout.direction); + + if (gPrintTree) { + print_css_node(node, CSS_PRINT_LAYOUT | CSS_PRINT_CHILDREN | CSS_PRINT_STYLE); + } } } - -void resetNodeLayout(css_node_t *node) { - node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED; - node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED; - node->layout.position[CSS_LEFT] = 0; - node->layout.position[CSS_TOP] = 0; -} diff --git a/src/Layout.h b/src/Layout.h index 1df58225..3af37e5f 100644 --- a/src/Layout.h +++ b/src/Layout.h @@ -44,6 +44,11 @@ typedef enum { CSS_JUSTIFY_SPACE_AROUND } css_justify_t; +typedef enum { + CSS_OVERFLOW_VISIBLE = 0, + CSS_OVERFLOW_HIDDEN +} css_overflow_t; + // Note: auto is only a valid value for alignSelf. It is NOT a valid value for // alignItems. typedef enum { @@ -79,7 +84,8 @@ typedef enum { typedef enum { CSS_MEASURE_MODE_UNDEFINED = 0, CSS_MEASURE_MODE_EXACTLY, - CSS_MEASURE_MODE_AT_MOST + CSS_MEASURE_MODE_AT_MOST, + CSS_MEASURE_MODE_COUNT } css_measure_mode_t; typedef enum { @@ -87,20 +93,40 @@ typedef enum { CSS_HEIGHT } css_dimension_t; +typedef struct { + float available_width; + float available_height; + css_measure_mode_t width_measure_mode; + css_measure_mode_t height_measure_mode; + + float computed_width; + float computed_height; +} css_cached_measurement_t; + +enum { + // This value was chosen based on empiracle data. Even the most complicated + // layouts should not require more than 16 entries to fit within the cache. + CSS_MAX_CACHED_RESULT_COUNT = 16 +}; + typedef struct { float position[4]; float dimensions[2]; css_direction_t direction; + float flex_basis; + // Instead of recomputing the entire layout every single time, we // cache some information to break early when nothing changed bool should_update; - float last_requested_dimensions[2]; - float last_parent_max_width; - float last_parent_max_height; - float last_dimensions[2]; - float last_position[2]; - css_direction_t last_direction; + int generation_count; + css_direction_t last_parent_direction; + + int next_cached_measurements_index; + css_cached_measurement_t cached_measurements[CSS_MAX_CACHED_RESULT_COUNT]; + float measured_dimensions[2]; + + css_cached_measurement_t cached_layout; } css_layout_t; typedef struct { @@ -116,6 +142,7 @@ typedef struct { css_align_t align_self; css_position_type_t position_type; css_wrap_type_t flex_wrap; + css_overflow_t overflow; float flex; float margin[6]; float position[4]; @@ -143,8 +170,7 @@ struct css_node { int children_count; int line_index; - css_node_t *next_absolute_child; - css_node_t *next_flex_child; + css_node_t* next_child; css_dim_t (*measure)(void *context, float width, css_measure_mode_t widthMode, float height, css_measure_mode_t heightMode); void (*print)(void *context); @@ -166,12 +192,8 @@ typedef enum { } css_print_options_t; void print_css_node(css_node_t *node, css_print_options_t options); +// Function that computes the layout! +void layoutNode(css_node_t *node, float availableWidth, float availableHeight, css_direction_t parentDirection); bool isUndefined(float value); -// Function that computes the layout! -void layoutNode(css_node_t *node, float maxWidth, float maxHeight, css_direction_t parentDirection); - -// Reset the calculated layout values for a given node. You should call this before `layoutNode`. -void resetNodeLayout(css_node_t *node); - #endif diff --git a/src/Layout.js b/src/Layout.js index e926592e..72a68360 100755 --- a/src/Layout.js +++ b/src/Layout.js @@ -1,4 +1,4 @@ -/** + /** * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * @@ -8,9 +8,18 @@ */ var computeLayout = (function() { - + + var POSITIVE_FLEX_IS_AUTO = false; + + var gCurrentGenerationCount = 0; + var CSS_UNDEFINED; - + + var CSS_LEFT = 'left'; + var CSS_TOP = 'top'; + var CSS_RIGHT = 'right'; + var CSS_BOTTOM = 'bottom'; + var CSS_DIRECTION_INHERIT = 'inherit'; var CSS_DIRECTION_LTR = 'ltr'; var CSS_DIRECTION_RTL = 'rtl'; @@ -33,7 +42,10 @@ var computeLayout = (function() { var CSS_POSITION_RELATIVE = 'relative'; var CSS_POSITION_ABSOLUTE = 'absolute'; - + + var CSS_OVERFLOW_VISIBLE = 'visible'; + var CSS_OVERFLOW_HIDDEN = 'hidden'; + var CSS_MEASURE_MODE_UNDEFINED = 'undefined'; var CSS_MEASURE_MODE_EXACTLY = 'exactly'; var CSS_MEASURE_MODE_AT_MOST = 'at-most'; @@ -62,6 +74,12 @@ var computeLayout = (function() { 'column': 'height', 'column-reverse': 'height' }; + var measuredDim = { + 'row': 'measuredWidth', + 'row-reverse': 'measuredWidth', + 'column': 'measuredHeight', + 'column-reverse': 'measuredHeight' + }; // When transpiled to Java / C the node type has layout, children and style // properties. For the JavaScript version this function adds these properties @@ -95,7 +113,7 @@ var computeLayout = (function() { } function isUndefined(value) { - return value === undefined || isNaN(value); + return value === undefined || Number.isNaN(value); } function isRowDirection(flexDirection) { @@ -107,6 +125,46 @@ var computeLayout = (function() { return flexDirection === CSS_FLEX_DIRECTION_COLUMN || flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE; } + + function getFlex(node) { + if (node.style.flex === undefined) { + return 0; + } + return node.style.flex; + } + + function isFlexBasisAuto(node) { + if (POSITIVE_FLEX_IS_AUTO) { + // All flex values are auto. + return true; + } else { + // A flex value > 0 implies a basis of zero. + return getFlex(node) <= 0; + } + } + + function getFlexGrowFactor(node) { + // Flex grow is implied by positive values for flex. + if (getFlex(node) > 0) { + return getFlex(node); + } + return 0; + } + + function getFlexShrinkFactor(node) { + if (POSITIVE_FLEX_IS_AUTO) { + // A flex shrink factor of 1 is implied by non-zero values for flex. + if (getFlex(node) !== 0) { + return 1; + } + } else { + // A flex shrink factor of 1 is implied by negative values for flex. + if (getFlex(node) < 0) { + return 1; + } + } + return 0; + } function getLeadingMargin(node, axis) { if (node.style.marginStart !== undefined && isRowDirection(axis)) { @@ -264,10 +322,6 @@ var computeLayout = (function() { return getTrailingPadding(node, axis) + getTrailingBorder(node, axis); } - function getBorderAxis(node, axis) { - return getLeadingBorder(node, axis) + getTrailingBorder(node, axis); - } - function getMarginAxis(node, axis) { return getLeadingMargin(node, axis) + getTrailingMargin(node, axis); } @@ -347,13 +401,20 @@ var computeLayout = (function() { if (node.style.position) { return node.style.position; } - return 'relative'; + return CSS_POSITION_RELATIVE; + } + + function getOverflow(node) { + if (node.style.overflow) { + return node.style.overflow; + } + return CSS_OVERFLOW_VISIBLE; } function isFlex(node) { return ( getPositionType(node) === CSS_POSITION_RELATIVE && - node.style.flex > 0 + node.style.flex !== undefined && node.style.flex !== 0 ); } @@ -362,15 +423,15 @@ var computeLayout = (function() { } function getDimWithMargin(node, axis) { - return node.layout[dim[axis]] + getMarginAxis(node, axis); + return node.layout[measuredDim[axis]] + getMarginAxis(node, axis); } - - function isStyleDimDefined(node, axis) { + + function isStyleDimDefined(node, axis) { return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0; } - - function isLayoutDimDefined(node, axis) { - return node.layout[dim[axis]] !== undefined && node.layout[dim[axis]] >= 0; + + function isLayoutDimDefined(node, axis) { + return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0; } function isPosDefined(node, pos) { @@ -387,8 +448,8 @@ var computeLayout = (function() { } return 0; } - - function boundAxis(node, axis, value) { + + function boundAxisWithinMinAndMax(node, axis, value) { var min = { 'row': node.style.minWidth, 'row-reverse': node.style.minWidth, @@ -412,6 +473,13 @@ var computeLayout = (function() { } return boundValue; } + + function fminf(a, b) { + if (a < b) { + return a; + } + return b; + } function fmaxf(a, b) { if (a > b) { @@ -419,28 +487,18 @@ var computeLayout = (function() { } return b; } - - // When the user specifically sets a value for width or height - function setDimensionFromStyle(node, axis) { - // The parent already computed us a width or height. We just skip it - if (isLayoutDimDefined(node, axis)) { - return; - } - // We only run if there's a width or height defined - if (!isStyleDimDefined(node, axis)) { - return; - } - - // The dimensions can never be smaller than the padding and border - node.layout[dim[axis]] = fmaxf( - boundAxis(node, axis, node.style[dim[axis]]), - getPaddingAndBorderAxis(node, axis) - ); + + // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the + // padding and border amount. + function boundAxis(node, axis, value) { + return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis)); } function setTrailingPosition(node, child, axis) { - child.layout[trailing[axis]] = node.layout[dim[axis]] - - child.layout[dim[axis]] - child.layout[pos[axis]]; + var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ? + 0 : + child.layout[measuredDim[axis]]; + child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]]; } // If both left and right are defined, then use left. Otherwise return @@ -451,352 +509,392 @@ var computeLayout = (function() { } return -getPosition(node, trailing[axis]); } + + function setPosition(node, direction) { + var mainAxis = resolveAxis(getFlexDirection(node), direction); + var crossAxis = getCrossFlexDirection(mainAxis, direction); + + node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) + + getRelativePosition(node, mainAxis); + node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) + + getRelativePosition(node, crossAxis); + } + + function assert(condition, message) { + if (!condition) { + throw new Error(message); + } + } + + // + // This is the main routine that implements a subset of the flexbox layout algorithm + // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/. + // + // Limitations of this algorithm, compared to the full standard: + // * Display property is always assumed to be 'flex' except for Text nodes, which + // are assumed to be 'inline-flex'. + // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are + // stacked in document order. + // * The 'order' property is not supported. The order of flex items is always defined + // by document order. + // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse' + // and 'hidden' are not supported. + // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The + // rarely-used 'wrap-reverse' is not supported. + // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and + // flexBasis, this algorithm supports only the three most common combinations: + // flex: 0 is equiavlent to flex: 0 0 auto + // flex: n (where n is a positive value) is equivalent to flex: n 1 auto + // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0 + // This is faster because the content doesn't need to be measured, but it's + // less flexible because the basis is always 0 and can't be overriden with + // the width/height attributes. + // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto + // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel + // values, and the default value is 0. + // * The 'baseline' value is not supported for alignItems and alignSelf properties. + // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be + // specified as pixel values, not as percentages. + // * There is no support for calculation of dimensions based on intrinsic aspect ratios + // (e.g. images). + // * There is no support for forced breaks. + // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text). + // + // Deviations from standard: + // * Section 4.5 of the spec indicates that all flex items have a default minimum + // main size. For text blocks, for example, this is the width of the widest word. + // Calculating the minimum width is expensive, so we forego it and assume a default + // minimum main size of 0. + // * Min/Max sizes in the main axis are not honored when resolving flexible lengths. + // * The spec indicates that the default value for 'flexDirection' is 'row', but + // the algorithm below assumes a default of 'column'. + // + // Input parameters: + // - node: current node to be sized and layed out + // - availableWidth & availableHeight: available size to be used for sizing the node + // or CSS_UNDEFINED if the size is not available; interpretation depends on layout + // flags + // - parentDirection: the inline (text) direction within the parent (left-to-right or + // right-to-left) + // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation) + // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation) + // - performLayout: specifies whether the caller is interested in just the dimensions + // of the node or it requires the entire node and its subtree to be layed out + // (with final positions) + // + // Details: + // This routine is called recursively to lay out subtrees of flexbox elements. It uses the + // information in node.style, which is treated as a read-only input. It is responsible for + // setting the layout.direction and layout.measured_dimensions fields for the input node as well + // as the layout.position and layout.line_index fields for its child nodes. The + // layout.measured_dimensions field includes any border or padding for the node but does + // not include margins. + // + // The spec describes four different layout modes: "fill available", "max content", "min content", + // and "fit content". Of these, we don't use "min content" because we don't support default + // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode + // from the spec (https://www.w3.org/TR/css3-sizing/#terms): + // - CSS_MEASURE_MODE_UNDEFINED: max content + // - CSS_MEASURE_MODE_EXACTLY: fill available + // - CSS_MEASURE_MODE_AT_MOST: fit content + // + // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of + // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension. + // + function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) { + assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED'); + assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED'); + + var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW); + var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); + var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); - function layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, /*css_direction_t*/parentDirection) { + // Set the resolved resolution in the node's layout. var/*css_direction_t*/ direction = resolveDirection(node, parentDirection); - var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction); - var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction); - var/*(c)!css_flex_direction_t*//*(java)!int*/ resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); - - // Handle width and height style attributes - setDimensionFromStyle(node, mainAxis); - setDimensionFromStyle(node, crossAxis); - - // Set the resolved resolution in the node's layout node.layout.direction = direction; - // The position is set by the parent, but we need to complete it with a - // delta composed of the margin and left/top/right/bottom - node.layout[leading[mainAxis]] += getLeadingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node.layout[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) + - getRelativePosition(node, mainAxis); - node.layout[leading[crossAxis]] += getLeadingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - node.layout[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) + - getRelativePosition(node, crossAxis); - - // Inline immutable values from the target node to avoid excessive method - // invocations during the layout calculation. - var/*int*/ childCount = node.children.length; - var/*float*/ paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis); - var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - + // For content (text) nodes, determine the dimensions based on the text contents. if (isMeasureDefined(node)) { - var/*bool*/ isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis); + var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + + if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) { - var/*float*/ width = CSS_UNDEFINED; - var/*css_measure_mode_t*/ widthMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, resolvedRowAxis)) { - width = node.style.width; - widthMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isResolvedRowDimDefined) { - width = node.layout[dim[resolvedRowAxis]]; - widthMode = CSS_MEASURE_MODE_EXACTLY; + // Don't bother sizing the text if both dimensions are already defined. + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); + } else if (innerWidth <= 0) { + + // Don't bother sizing the text if there's no horizontal space. + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { - width = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis); - widthMode = CSS_MEASURE_MODE_AT_MOST; - } - width -= paddingAndBorderAxisResolvedRow; - if (isUndefined(width)) { - widthMode = CSS_MEASURE_MODE_UNDEFINED; - } - var/*float*/ height = CSS_UNDEFINED; - var/*css_measure_mode_t*/ heightMode = CSS_MEASURE_MODE_UNDEFINED; - if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node.style.height; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - height = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]]; - heightMode = CSS_MEASURE_MODE_EXACTLY; - } else { - height = parentMaxHeight - - getMarginAxis(node, resolvedRowAxis); - heightMode = CSS_MEASURE_MODE_AT_MOST; - } - height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN); - if (isUndefined(height)) { - heightMode = CSS_MEASURE_MODE_UNDEFINED; - } - - // We only need to give a dimension for the text if we haven't got any - // for it computed yet. It can either be from the style attribute or because - // the element is flexible. - var/*bool*/ isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined; - var/*bool*/ isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) && - isUndefined(node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]]); - - // Let's not measure the text if we already know both dimensions - if (isRowUndefined || isColumnUndefined) { + // Measure the text under the current constraints. var/*css_dim_t*/ measureDim = node.style.measure( /*(c)!node->context,*/ /*(java)!layoutContext.measureOutput,*/ - width, - widthMode, - height, - heightMode + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode ); - if (isRowUndefined) { - node.layout.width = measureDim.width + - paddingAndBorderAxisResolvedRow; - } - if (isColumnUndefined) { - node.layout.height = measureDim.height + - paddingAndBorderAxisColumn; - } + + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + measureDim.width + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + measureDim.height + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); } - if (childCount === 0) { + + return; + } + + // For nodes with no children, use the available values if they were provided, or + // the minimum size as indicated by the padding and border sizes. + var/*int*/ childCount = node.children.length; + if (childCount === 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ? + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); + return; + } + + // If we're not being asked to perform a full layout, we can handle a number of common + // cases here without incurring the cost of the remaining function. + if (!performLayout) { + // If we're being asked to size the content with an at most constraint but there is no available width, + // the measurement will always be zero. + if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 && + heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn)); + return; + } + + if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow)); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + // If we're being asked to use an exact width/height, there's no need to measure the children. + if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) { + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); return; } } + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction); + var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction); + var/*bool*/ isMainAxisRow = isRowDirection(mainAxis); + var/*css_justify_t*/ justifyContent = getJustifyContent(node); var/*bool*/ isNodeFlexWrap = isFlexWrap(node); - var/*css_justify_t*/ justifyContent = getJustifyContent(node); + var/*css_node_t**/ firstAbsoluteChild = undefined; + var/*css_node_t**/ currentAbsoluteChild = undefined; var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis); + var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis); var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis); var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis); var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis); + + var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; - var/*bool*/ isMainDimDefined = isLayoutDimDefined(node, mainAxis); - var/*bool*/ isCrossDimDefined = isLayoutDimDefined(node, crossAxis); - var/*bool*/ isMainRowDirection = isRowDirection(mainAxis); + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; + var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; - var/*int*/ i; - var/*int*/ ii; + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM var/*css_node_t**/ child; - var/*(c)!css_flex_direction_t*//*(java)!int*/ axis; + var/*int*/ i; + var/*float*/ childWidth; + var/*float*/ childHeight; + var/*css_measure_mode_t*/ childWidthMeasureMode; + var/*css_measure_mode_t*/ childHeightMeasureMode; + for (i = 0; i < childCount; i++) { + child = node.children[i]; - var/*css_node_t**/ firstAbsoluteChild = null; - var/*css_node_t**/ currentAbsoluteChild = null; + if (performLayout) { + // Set the initial position (relative to the parent). + var/*css_direction_t*/ childDirection = resolveDirection(child, direction); + setPosition(child, childDirection); + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if (getPositionType(child) === CSS_POSITION_ABSOLUTE) { - var/*float*/ definedMainDim = CSS_UNDEFINED; - if (isMainDimDefined) { - definedMainDim = node.layout[dim[mainAxis]] - paddingAndBorderAxisMain; + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild === undefined) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild !== undefined) { + currentAbsoluteChild.nextChild = child; + } + currentAbsoluteChild = child; + child.nextChild = undefined; + } else { + + if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + + // The width is definite, so use that as the flex basis. + child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW)); + } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + + // The height is definite, so use that as the flex basis. + child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN)); + } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) { + + // If the basis isn't 'auto', it is assumed to be zero. + child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis)); + } else { + + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED; + + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) { + childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + // Measure the child + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure'); + + child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis)); + } + } } - // We want to execute the next two loops one per line with flex-wrap - var/*int*/ startLine = 0; - var/*int*/ endLine = 0; - // var/*int*/ nextOffset = 0; - var/*int*/ alreadyComputedNextLayout = 0; - // We aggregate the total dimensions of the container in those two variables - var/*float*/ linesCrossDim = 0; - var/*float*/ linesMainDim = 0; - var/*int*/ linesCount = 0; - while (endLine < childCount) { - // Layout non flexible children and count children by type + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + var/*int*/ startOfLineIndex = 0; + var/*int*/ endOfLineIndex = 0; + + // Number of lines. + var/*int*/ lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + var/*float*/ totalLineCrossDim = 0; - // mainContentDim is accumulation of the dimensions and margin of all the - // non flexible children. This will be used in order to either set the - // dimensions of the node if none already exist, or to compute the - // remaining space left for the flexible children. - var/*float*/ mainContentDim = 0; + // Max main dimension of all the lines. + var/*float*/ maxLineMainDim = 0; - // There are three kind of children, non flexible, flexible and absolute. - // We need to know how many there are in order to distribute the space. - var/*int*/ flexibleChildrenCount = 0; - var/*float*/ totalFlexible = 0; - var/*int*/ nonFlexibleChildrenCount = 0; + while (endOfLineIndex < childCount) { + + // Number of items on the currently line. May be different than the difference + // between start and end indicates because we skip over absolute-positioned items. + var/*int*/ itemsOnLine = 0; - // Use the line loop to position children in the main axis for as long - // as they are using a simple stacking behaviour. Children that are - // immediately stacked in the initial loop will not be touched again - // in . - var/*bool*/ isSimpleStackMain = - (isMainDimDefined && justifyContent === CSS_JUSTIFY_FLEX_START) || - (!isMainDimDefined && justifyContent !== CSS_JUSTIFY_CENTER); - var/*int*/ firstComplexMain = (isSimpleStackMain ? childCount : startLine); + // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin + // of all the children on the current line. This will be used in order to + // either set the dimensions of the node if none already exist or to compute + // the remaining space left for the flexible children. + var/*float*/ sizeConsumedOnCurrentLine = 0; - // Use the initial line loop to position children in the cross axis for - // as long as they are relatively positioned with alignment STRETCH or - // FLEX_START. Children that are immediately stacked in the initial loop - // will not be touched again in . - var/*bool*/ isSimpleStackCross = true; - var/*int*/ firstComplexCross = childCount; + var/*float*/ totalFlexGrowFactors = 0; + var/*float*/ totalFlexShrinkScaledFactors = 0; - var/*css_node_t**/ firstFlexChild = null; - var/*css_node_t**/ currentFlexChild = null; + i = startOfLineIndex; - var/*float*/ mainDim = leadingPaddingAndBorderMain; - var/*float*/ crossDim = 0; + // Maintain a linked list of the child nodes that can shrink and/or grow. + var/*css_node_t**/ firstRelativeChild = undefined; + var/*css_node_t**/ currentRelativeChild = undefined; - var/*float*/ maxWidth = CSS_UNDEFINED; - var/*float*/ maxHeight = CSS_UNDEFINED; - for (i = startLine; i < childCount; ++i) { + // Add items to the current line until it's full or we run out of items. + while (i < childCount) { child = node.children[i]; - child.lineIndex = linesCount; + child.lineIndex = lineCount; - child.nextAbsoluteChild = null; - child.nextFlexChild = null; - - var/*css_align_t*/ alignItem = getAlignItem(node, child); - - // Pre-fill cross axis dimensions when the child is using stretch before - // we call the recursive layout pass - if (alignItem === CSS_ALIGN_STRETCH && - getPositionType(child) === CSS_POSITION_RELATIVE && - isCrossDimDefined && - !isStyleDimDefined(child, crossAxis)) { - child.layout[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, node.layout[dim[crossAxis]] - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - } else if (getPositionType(child) === CSS_POSITION_ABSOLUTE) { - // Store a private linked list of absolutely positioned children - // so that we can efficiently traverse them later. - if (firstAbsoluteChild === null) { - firstAbsoluteChild = child; + if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) { + var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis); + + // If this is a multi-line flow and this item pushes us over the available size, we've + // hit the end of the current line. Break out of the loop and lay out the current line. + if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) { + break; } - if (currentAbsoluteChild !== null) { - currentAbsoluteChild.nextAbsoluteChild = child; - } - currentAbsoluteChild = child; - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(child, axis) && - isPosDefined(child, leading[axis]) && - isPosDefined(child, trailing[axis])) { - child.layout[dim[axis]] = fmaxf( - boundAxis(child, axis, node.layout[dim[axis]] - - getPaddingAndBorderAxis(node, axis) - - getMarginAxis(child, axis) - - getPosition(child, leading[axis]) - - getPosition(child, trailing[axis])), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, axis) - ); - } + sizeConsumedOnCurrentLine += outerFlexBasis; + itemsOnLine++; + + if (isFlex(child)) { + totalFlexGrowFactors += getFlexGrowFactor(child); + + // Unlike the grow factor, the shrink factor is scaled relative to the child + // dimension. + totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis; } + + // Store a private linked list of children that need to be layed out. + if (firstRelativeChild === undefined) { + firstRelativeChild = child; + } + if (currentRelativeChild !== undefined) { + currentRelativeChild.nextChild = child; + } + currentRelativeChild = child; + child.nextChild = undefined; } - - var/*float*/ nextContentDim = 0; - - // It only makes sense to consider a child flexible if we have a computed - // dimension for the node. - if (isMainDimDefined && isFlex(child)) { - flexibleChildrenCount++; - totalFlexible += child.style.flex; - - // Store a private linked list of flexible children so that we can - // efficiently traverse them later. - if (firstFlexChild === null) { - firstFlexChild = child; - } - if (currentFlexChild !== null) { - currentFlexChild.nextFlexChild = child; - } - currentFlexChild = child; - - // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information, which represents - // the smallest possible size for the child, to compute the remaining - // available space. - nextContentDim = getPaddingAndBorderAxis(child, mainAxis) + - getMarginAxis(child, mainAxis); - - } else { - maxWidth = CSS_UNDEFINED; - maxHeight = CSS_UNDEFINED; - - if (!isMainRowDirection) { - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node.layout[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - } else { - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - } - - // This is the main recursive call. We layout non flexible children. - if (alreadyComputedNextLayout === 0) { - layoutNode(/*(java)!layoutContext, */child, maxWidth, maxHeight, direction); - } - - // Absolute positioned elements do not take part of the layout, so we - // don't use them to compute mainContentDim - if (getPositionType(child) === CSS_POSITION_RELATIVE) { - nonFlexibleChildrenCount++; - // At this point we know the final size and margin of the element. - nextContentDim = getDimWithMargin(child, mainAxis); - } - } - - // The element we are about to add would make us go to the next line - if (isNodeFlexWrap && - isMainDimDefined && - mainContentDim + nextContentDim > definedMainDim && - // If there's only one element, then it's bigger than the content - // and needs its own line - i !== startLine) { - nonFlexibleChildrenCount--; - alreadyComputedNextLayout = 1; - break; - } - - // Disable simple stacking in the main axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackMain && - (getPositionType(child) !== CSS_POSITION_RELATIVE || isFlex(child))) { - isSimpleStackMain = false; - firstComplexMain = i; - } - - // Disable simple stacking in the cross axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackCross && - (getPositionType(child) !== CSS_POSITION_RELATIVE || - (alignItem !== CSS_ALIGN_STRETCH && alignItem !== CSS_ALIGN_FLEX_START) || - (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) { - isSimpleStackCross = false; - firstComplexCross = i; - } - - if (isSimpleStackMain) { - child.layout[pos[mainAxis]] += mainDim; - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); - } - - mainDim += getDimWithMargin(child, mainAxis); - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); - } - - if (isSimpleStackCross) { - child.layout[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); - } - } - - alreadyComputedNextLayout = 0; - mainContentDim += nextContentDim; - endLine = i + 1; + + i++; + endOfLineIndex++; } - - // Layout flexible children and allocate empty space + + // If we don't need to measure the cross axis, we can skip the entire flex step. + var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY; // In order to position the elements in the main axis, we have two // controls. The space between the beginning and the first element @@ -804,212 +902,300 @@ var computeLayout = (function() { var/*float*/ leadingMainDim = 0; var/*float*/ betweenMainDim = 0; - // The remaining available space that needs to be allocated - var/*float*/ remainingMainDim = 0; - if (isMainDimDefined) { - remainingMainDim = definedMainDim - mainContentDim; - } else { - remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim; + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + var/*float*/ remainingFreeSpace = 0; + if (!isUndefined(availableInnerMainDim)) { + remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; + } else if (sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized based on its content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine; + } + + var/*float*/ remainingFreeSpaceAfterFlex = remainingFreeSpace; + + if (!canSkipFlex) { + var/*float*/ childFlexBasis; + var/*float*/ flexShrinkScaledFactor; + var/*float*/ flexGrowFactor; + var/*float*/ baseMainSize; + var/*float*/ boundMainSize; + + // Do two passes over the flex items to figure out how to distribute the remaining space. + // The first pass finds the items whose min/max constraints trigger, freezes them at those + // sizes, and excludes those sizes from the remaining space. The second pass sets the size + // of each flexible item. It distributes the remaining space amongst the items whose min/max + // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing + // their min/max constraints to trigger again. + // + // This two pass approach for resolving min/max constraints deviates from the spec. The + // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process + // that needs to be repeated a variable number of times. The algorithm implemented here + // won't handle all cases but it was simpler to implement and it mitigates performance + // concerns because we know exactly how many passes it'll do. + + // First pass: detect the flex items whose min/max constraints trigger + var/*float*/ deltaFreeSpace = 0; + var/*float*/ deltaFlexShrinkScaledFactors = 0; + var/*float*/ deltaFlexGrowFactors = 0; + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild !== undefined) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor !== 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize !== boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; + } + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor !== 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize !== boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexGrowFactors -= flexGrowFactor; + } + } + } + + currentRelativeChild = currentRelativeChild.nextChild; + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; + totalFlexGrowFactors += deltaFlexGrowFactors; + remainingFreeSpace += deltaFreeSpace; + remainingFreeSpaceAfterFlex = remainingFreeSpace; + + // Second pass: resolve the sizes of the flexible items + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild !== undefined) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + var/*float*/ updatedMainSize = childFlexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor !== 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor); + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor !== 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor); + } + } + + remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + + if (isMainAxisRow) { + childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } else { + childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN); + childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY; + + if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST; + } else { + childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); + childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY; + } + } + + var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) && + getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH; + + // Recursively call the layout algorithm for this child with the updated main size. + layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex'); + + currentRelativeChild = currentRelativeChild.nextChild; + } + } + + remainingFreeSpace = remainingFreeSpaceAfterFlex; + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main axis. + // Their dimensions are also set in the cross axis with the exception of items + // that are aligned 'stretch'. We need to compute these stretch values and + // set the final positions. + + // If we are using "at most" rules in the main axis, we won't distribute + // any remaining space at this point. + if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) { + remainingFreeSpace = 0; } - // If there are flexible children in the mix, they are going to fill the - // remaining space - if (flexibleChildrenCount !== 0) { - var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible; - var/*float*/ baseMainDim; - var/*float*/ boundMainDim; - - // If the flex share of remaining space doesn't meet min/max bounds, - // remove this child from flex calculations. - currentFlexChild = firstFlexChild; - while (currentFlexChild !== null) { - baseMainDim = flexibleMainDim * currentFlexChild.style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis); - boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); - - if (baseMainDim !== boundMainDim) { - remainingMainDim -= boundMainDim; - totalFlexible -= currentFlexChild.style.flex; - } - - currentFlexChild = currentFlexChild.nextFlexChild; - } - flexibleMainDim = remainingMainDim / totalFlexible; - - // The non flexible children can overflow the container, in this case - // we should just assume that there is no space available. - if (flexibleMainDim < 0) { - flexibleMainDim = 0; - } - - currentFlexChild = firstFlexChild; - while (currentFlexChild !== null) { - // At this point we know the final size of the element in the main - // dimension - currentFlexChild.layout[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, - flexibleMainDim * currentFlexChild.style.flex + - getPaddingAndBorderAxis(currentFlexChild, mainAxis) - ); - - maxWidth = CSS_UNDEFINED; - if (isLayoutDimDefined(node, resolvedRowAxis)) { - maxWidth = node.layout[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - getMarginAxis(node, resolvedRowAxis) - - paddingAndBorderAxisResolvedRow; - } - maxHeight = CSS_UNDEFINED; - if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { - maxHeight = node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else if (isMainRowDirection) { - maxHeight = parentMaxHeight - - getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) - - paddingAndBorderAxisColumn; - } - - // And we recursively call the layout algorithm for this child - layoutNode(/*(java)!layoutContext, */currentFlexChild, maxWidth, maxHeight, direction); - - child = currentFlexChild; - currentFlexChild = currentFlexChild.nextFlexChild; - child.nextFlexChild = null; - } - - // We use justifyContent to figure out how to allocate the remaining - // space available - } else if (justifyContent !== CSS_JUSTIFY_FLEX_START) { + // Use justifyContent to figure out how to allocate the remaining space + // available in the main axis. + if (justifyContent !== CSS_JUSTIFY_FLEX_START) { if (justifyContent === CSS_JUSTIFY_CENTER) { - leadingMainDim = remainingMainDim / 2; + leadingMainDim = remainingFreeSpace / 2; } else if (justifyContent === CSS_JUSTIFY_FLEX_END) { - leadingMainDim = remainingMainDim; + leadingMainDim = remainingFreeSpace; } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) { - remainingMainDim = fmaxf(remainingMainDim, 0); - if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 !== 0) { - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + remainingFreeSpace = fmaxf(remainingFreeSpace, 0); + if (itemsOnLine > 1) { + betweenMainDim = remainingFreeSpace / (itemsOnLine - 1); } else { betweenMainDim = 0; } } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) { // Space on the edges is half of the space between elements - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount); + betweenMainDim = remainingFreeSpace / itemsOnLine; leadingMainDim = betweenMainDim / 2; } } - // Position elements in the main axis and compute dimensions + var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim; + var/*float*/ crossDim = 0; - // At this point, all the children have their dimensions set. We need to - // find their position. In order to do that, we accumulate data in - // variables that are also useful to compute the total dimensions of the - // container! - mainDim += leadingMainDim; - - for (i = firstComplexMain; i < endLine; ++i) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { child = node.children[i]; if (getPositionType(child) === CSS_POSITION_ABSOLUTE && isPosDefined(child, leading[mainAxis])) { - // In case the child is position absolute and has left/top being - // defined, we override the position to whatever the user said - // (and margin/border). - child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + - getLeadingBorder(node, mainAxis) + - getLeadingMargin(child, mainAxis); - } else { - // If the child is position absolute (without top/left) or relative, - // we put it at the current accumulated offset. - child.layout[pos[mainAxis]] += mainDim; - - // Define the trailing position accordingly. - if (isMainDimDefined) { - setTrailingPosition(node, child, mainAxis); + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) + + getLeadingBorder(node, mainAxis) + + getLeadingMargin(child, mainAxis); } - - // Now that we placed the element, we need to update the variables - // We only need to do that for relative elements. Absolute elements + } else { + if (performLayout) { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child.layout[pos[mainAxis]] += mainDim; + } + + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements // do not take part in that phase. if (getPositionType(child) === CSS_POSITION_RELATIVE) { - // The main dimension is the sum of all the elements dimension plus - // the spacing. - mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); - // The cross dimension is the max of the elements dimension since there - // can only be one element in that cross dimension. - crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis))); + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims because + // they weren't computed. This means we can't call getDimWithMargin. + mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis; + crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + getDimWithMargin(child, mainAxis); + + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis)); + } } } } - var/*float*/ containerCrossAxis = node.layout[dim[crossAxis]]; - if (!isCrossDimDefined) { - containerCrossAxis = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); + mainDim += trailingPaddingAndBorderMain; + + var/*float*/ containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; + + if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) { + containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim); + } } - // Position elements in the cross axis - for (i = firstComplexCross; i < endLine; ++i) { - child = node.children[i]; + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) { + crossDim = availableInnerCrossDim; + } - if (getPositionType(child) === CSS_POSITION_ABSOLUTE && - isPosDefined(child, leading[crossAxis])) { - // In case the child is absolutely positionned and has a - // top/left/bottom/right being set, we override all the previously - // computed positions to set it correctly. - child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + - getLeadingBorder(node, crossAxis) + - getLeadingMargin(child, crossAxis); + // Clamp to the min/max size specified on the container. + crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; - } else { - var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross; + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { + child = node.children[i]; - // For a relative children, we're either using alignItems (parent) or - // alignSelf (child) in order to determine the position in the cross axis - if (getPositionType(child) === CSS_POSITION_RELATIVE) { - /*eslint-disable */ - // This variable is intentionally re-defined as the code is transpiled to a block scope language + if (getPositionType(child) === CSS_POSITION_ABSOLUTE) { + // If the child is absolutely positioned and has a top/left/bottom/right + // set, override all the previously computed positions to set it correctly. + if (isPosDefined(child, leading[crossAxis])) { + child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) + + getLeadingBorder(node, crossAxis) + + getLeadingMargin(child, crossAxis); + } else { + child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross + + getLeadingMargin(child, crossAxis); + } + } else { + var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis var/*css_align_t*/ alignItem = getAlignItem(node, child); - /*eslint-enable */ + + // If the child uses align stretch, we need to lay it out one more time, this time + // forcing the cross-axis size to be the computed cross size for the current line. if (alignItem === CSS_ALIGN_STRETCH) { - // You can only stretch if the dimension has not already been defined - // previously. - if (!isStyleDimDefined(child, crossAxis)) { - var/*float*/ dimCrossAxis = child.layout[dim[crossAxis]]; - child.layout[dim[crossAxis]] = fmaxf( - boundAxis(child, crossAxis, containerCrossAxis - - paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)), - // You never want to go smaller than padding - getPaddingAndBorderAxis(child, crossAxis) - ); - - // If the size has changed, and this child has children we need to re-layout this child - if (dimCrossAxis != child.layout[dim[crossAxis]] && child.children.length > 0) { - // Reset child margins before re-layout as they are added back in layoutNode and would be doubled - child.layout[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child.layout[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) + - getRelativePosition(child, mainAxis); - child.layout[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - child.layout[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) + - getRelativePosition(child, crossAxis); - - layoutNode(/*(java)!layoutContext, */child, maxWidth, maxHeight, direction); - } + childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW); + childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN); + var/*bool*/ isCrossSizeDefinite = false; + + if (isMainAxisRow) { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN); + childHeight = crossDim; + } else { + isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW); + childWidth = crossDim; + } + + // If the child defines a definite size for its cross axis, there's no need to stretch. + if (!isCrossSizeDefinite) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch'); } } else if (alignItem !== CSS_ALIGN_FLEX_START) { - // The remaining space between the parent dimensions+padding and child - // dimensions+margin. - var/*float*/ remainingCrossDim = containerCrossAxis - - paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis); + var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis); if (alignItem === CSS_ALIGN_CENTER) { leadingCrossDim += remainingCrossDim / 2; @@ -1017,41 +1203,25 @@ var computeLayout = (function() { leadingCrossDim += remainingCrossDim; } } - } - // And we apply the position - child.layout[pos[crossAxis]] += linesCrossDim + leadingCrossDim; - - // Define the trailing position accordingly. - if (isCrossDimDefined) { - setTrailingPosition(node, child, crossAxis); + // And we apply the position + child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; } } } - linesCrossDim += crossDim; - linesMainDim = fmaxf(linesMainDim, mainDim); - linesCount += 1; - startLine = endLine; + totalLineCrossDim += crossDim; + maxLineMainDim = fmaxf(maxLineMainDim, mainDim); + + // Reset variables for new line. + lineCount++; + startOfLineIndex = endOfLineIndex; + endOfLineIndex = startOfLineIndex; } - // - // - // Note(prenaux): More than one line, we need to layout the crossAxis - // according to alignContent. - // - // Note that we could probably remove and handle the one line case - // here too, but for the moment this is safer since it won't interfere with - // previously working code. - // - // See specs: - // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm - // section 9.4 - // - if (linesCount > 1 && isCrossDimDefined) { - var/*float*/ nodeCrossAxisInnerSize = node.layout[dim[crossAxis]] - - paddingAndBorderAxisCross; - var/*float*/ remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) { + var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; var/*float*/ crossDimLead = 0; var/*float*/ currentLead = leadingPaddingAndBorderCross; @@ -1062,19 +1232,20 @@ var computeLayout = (function() { } else if (alignContent === CSS_ALIGN_CENTER) { currentLead += remainingAlignContentDim / 2; } else if (alignContent === CSS_ALIGN_STRETCH) { - if (nodeCrossAxisInnerSize > linesCrossDim) { - crossDimLead = (remainingAlignContentDim / linesCount); + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = (remainingAlignContentDim / lineCount); } } var/*int*/ endIndex = 0; - for (i = 0; i < linesCount; ++i) { + for (i = 0; i < lineCount; ++i) { var/*int*/ startIndex = endIndex; + var/*int*/ j; // compute the line's height and find the endIndex var/*float*/ lineHeight = 0; - for (ii = startIndex; ii < childCount; ++ii) { - child = node.children[ii]; + for (j = startIndex; j < childCount; ++j) { + child = node.children[j]; if (getPositionType(child) !== CSS_POSITION_RELATIVE) { continue; } @@ -1082,33 +1253,33 @@ var computeLayout = (function() { break; } if (isLayoutDimDefined(child, crossAxis)) { - lineHeight = fmaxf( - lineHeight, - child.layout[dim[crossAxis]] + getMarginAxis(child, crossAxis) - ); + lineHeight = fmaxf(lineHeight, + child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis)); } } - endIndex = ii; + endIndex = j; lineHeight += crossDimLead; - for (ii = startIndex; ii < endIndex; ++ii) { - child = node.children[ii]; - if (getPositionType(child) !== CSS_POSITION_RELATIVE) { - continue; - } + if (performLayout) { + for (j = startIndex; j < endIndex; ++j) { + child = node.children[j]; + if (getPositionType(child) !== CSS_POSITION_RELATIVE) { + continue; + } - var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child); - if (alignContentAlignItem === CSS_ALIGN_FLEX_START) { - child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) { - child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[dim[crossAxis]]; - } else if (alignContentAlignItem === CSS_ALIGN_CENTER) { - var/*float*/ childHeight = child.layout[dim[crossAxis]]; - child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; - } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) { - child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); - // TODO(prenaux): Correctly set the height of items with undefined - // (auto) crossAxis dimension. + var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem === CSS_ALIGN_FLEX_START) { + child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) { + child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]]; + } else if (alignContentAlignItem === CSS_ALIGN_CENTER) { + childHeight = child.layout[measuredDim[crossAxis]]; + child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) { + child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis); + // TODO(prenaux): Correctly set the height of items with indefinite + // (auto) crossAxis dimension. + } } } @@ -1116,138 +1287,266 @@ var computeLayout = (function() { } } - var/*bool*/ needsMainTrailingPos = false; - var/*bool*/ needsCrossTrailingPos = false; + // STEP 9: COMPUTING FINAL DIMENSIONS + node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (!isMainDimDefined) { - node.layout[dim[mainAxis]] = fmaxf( - // We're missing the last padding at this point to get the final - // dimension - boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)), - // We can never assign a width smaller than the padding and borders - paddingAndBorderAxisMain - ); + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim); + } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) { + node.layout[measuredDim[mainAxis]] = fmaxf( + fminf(availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)), + paddingAndBorderAxisMain); + } + + if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross); + } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) { + node.layout[measuredDim[crossAxis]] = fmaxf( + fminf(availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)), + paddingAndBorderAxisCross); + } + + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + if (performLayout) { + var/*bool*/ needsMainTrailingPos = false; + var/*bool*/ needsCrossTrailingPos = false; if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE || mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsMainTrailingPos = true; } - } - - if (!isCrossDimDefined) { - node.layout[dim[crossAxis]] = fmaxf( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE || crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsCrossTrailingPos = true; } - } - // Set trailing position if necessary - if (needsMainTrailingPos || needsCrossTrailingPos) { - for (i = 0; i < childCount; ++i) { - child = node.children[i]; + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node.children[i]; - if (needsMainTrailingPos) { - setTrailingPosition(node, child, mainAxis); - } + if (needsMainTrailingPos) { + setTrailingPosition(node, child, mainAxis); + } - if (needsCrossTrailingPos) { - setTrailingPosition(node, child, crossAxis); + if (needsCrossTrailingPos) { + setTrailingPosition(node, child, crossAxis); + } } } } - - // Calculate dimensions for absolutely positioned elements + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN currentAbsoluteChild = firstAbsoluteChild; - while (currentAbsoluteChild !== null) { - // Pre-fill dimensions when using absolute position and both offsets for - // the axis are defined (either both left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + while (currentAbsoluteChild !== undefined) { + // Now that we know the bounds of the container, perform layout again on the + // absolutely-positioned children. + if (performLayout) { - if (isLayoutDimDefined(node, axis) && - !isStyleDimDefined(currentAbsoluteChild, axis) && - isPosDefined(currentAbsoluteChild, leading[axis]) && - isPosDefined(currentAbsoluteChild, trailing[axis])) { - currentAbsoluteChild.layout[dim[axis]] = fmaxf( - boundAxis(currentAbsoluteChild, axis, node.layout[dim[axis]] - - getBorderAxis(node, axis) - - getMarginAxis(currentAbsoluteChild, axis) - - getPosition(currentAbsoluteChild, leading[axis]) - - getPosition(currentAbsoluteChild, trailing[axis]) - ), - // You never want to go smaller than padding - getPaddingAndBorderAxis(currentAbsoluteChild, axis) - ); + childWidth = CSS_UNDEFINED; + childHeight = CSS_UNDEFINED; + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) { + childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + } else { + // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) { + childWidth = node.layout.measuredWidth - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) - + (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]); + childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth); + } + } + + if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) { + childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } else { + // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined. + if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) { + childHeight = node.layout.measuredHeight - + (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) - + (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]); + childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight); + } } - if (isPosDefined(currentAbsoluteChild, trailing[axis]) && - !isPosDefined(currentAbsoluteChild, leading[axis])) { - currentAbsoluteChild.layout[leading[axis]] = - node.layout[dim[axis]] - - currentAbsoluteChild.layout[dim[axis]] - - getPosition(currentAbsoluteChild, trailing[axis]); + // If we're still missing one or the other dimension, measure the content. + if (isUndefined(childWidth) || isUndefined(childHeight)) { + childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) { + if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST; + } + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure'); + childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW); + childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN); + } + + layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout'); + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) { + currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] = + node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] - + currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]); + } + + if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) && + !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) { + currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] = + node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] - + currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] - + getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]); } } - child = currentAbsoluteChild; - currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild; - child.nextAbsoluteChild = null; + currentAbsoluteChild = currentAbsoluteChild.nextChild; } } + + // + // This is a wrapper around the layoutNodeImpl function. It determines + // whether the layout request is redundant and can be skipped. + // + // Parameters: + // Input parameters are the same as layoutNodeImpl (see above) + // Return parameter is true if layout was performed, false if skipped + // + function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, + widthMeasureMode, heightMeasureMode, performLayout, reason) { + var layout = node.layout; - function layoutNode(node, parentMaxWidth, parentMaxHeight, parentDirection) { - node.shouldUpdate = true; + var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) || + layout.lastParentDirection !== parentDirection; - var direction = node.style.direction || CSS_DIRECTION_LTR; - var skipLayout = - !node.isDirty && - node.lastLayout && - node.lastLayout.requestedHeight === node.layout.height && - node.lastLayout.requestedWidth === node.layout.width && - node.lastLayout.parentMaxWidth === parentMaxWidth && - node.lastLayout.parentMaxHeight === parentMaxHeight && - node.lastLayout.direction === direction; - - if (skipLayout) { - node.layout.width = node.lastLayout.width; - node.layout.height = node.lastLayout.height; - node.layout.top = node.lastLayout.top; - node.layout.left = node.lastLayout.left; - } else { - if (!node.lastLayout) { - node.lastLayout = {}; + if (needToVisitNode) { + // Invalidate the cached results. + if (layout.cachedMeasurements !== undefined) { + layout.cachedMeasurements = []; } + if (layout.cachedLayout !== undefined) { + layout.cachedLayout.widthMeasureMode = undefined; + layout.cachedLayout.heightMeasureMode = undefined; + } + } - node.lastLayout.requestedWidth = node.layout.width; - node.lastLayout.requestedHeight = node.layout.height; - node.lastLayout.parentMaxWidth = parentMaxWidth; - node.lastLayout.parentMaxHeight = parentMaxHeight; - node.lastLayout.direction = direction; - - // Reset child layouts - node.children.forEach(function(child) { - child.layout.width = undefined; - child.layout.height = undefined; - child.layout.top = 0; - child.layout.left = 0; - }); - - layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, parentDirection); - - node.lastLayout.width = node.layout.width; - node.lastLayout.height = node.layout.height; - node.lastLayout.top = node.layout.top; - node.lastLayout.left = node.layout.left; + var cachedResults; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the positions + // and dimensions for nodes in the subtree. The algorithm assumes that each node + // gets layed out a maximum of one time per tree layout, but multiple measurements + // may be required to resolve all of the flex dimensions. + if (performLayout) { + if (layout.cachedLayout && + layout.cachedLayout.availableWidth === availableWidth && + layout.cachedLayout.availableHeight === availableHeight && + layout.cachedLayout.widthMeasureMode === widthMeasureMode && + layout.cachedLayout.heightMeasureMode === heightMeasureMode) { + cachedResults = layout.cachedLayout; + } + } else if (layout.cachedMeasurements) { + for (var i = 0, len = layout.cachedMeasurements.length; i < len; i++) { + if (layout.cachedMeasurements[i].availableWidth === availableWidth && + layout.cachedMeasurements[i].availableHeight === availableHeight && + layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode && + layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) { + cachedResults = layout.cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults !== undefined) { + layout.measureWidth = cachedResults.computedWidth; + layout.measureHeight = cachedResults.computedHeight; + } else { + layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout); + layout.lastParentDirection = parentDirection; + + if (cachedResults === undefined) { + var newCacheEntry; + if (performLayout) { + // Use the single layout cache entry. + if (layout.cachedLayout === undefined) { + layout.cachedLayout = {}; + } + newCacheEntry = layout.cachedLayout; + } else { + // Allocate a new measurement cache entry. + if (layout.cachedMeasurements === undefined) { + layout.cachedMeasurements = []; + } + newCacheEntry = {}; + layout.cachedMeasurements.push(newCacheEntry); + } + + newCacheEntry.availableWidth = availableWidth; + newCacheEntry.availableHeight = availableHeight; + newCacheEntry.widthMeasureMode = widthMeasureMode; + newCacheEntry.heightMeasureMode = heightMeasureMode; + newCacheEntry.computedWidth = layout.measuredWidth; + newCacheEntry.computedHeight = layout.measuredHeight; + } + } + + if (performLayout) { + node.layout.width = node.layout.measuredWidth; + node.layout.height = node.layout.measuredHeight; + layout.shouldUpdate = true; + } + + layout.generationCount = gCurrentGenerationCount; + return (needToVisitNode || cachedResults === undefined); + } + + function layoutNode(node, availableWidth, availableHeight, parentDirection) { + // Increment the generation count. This will force the recursive routine to visit + // all dirty nodes at least once. Subsequent visits will be skipped if the input + // parameters don't change. + gCurrentGenerationCount++; + + // If the caller didn't specify a height/width, use the dimensions + // specified in the style. + if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) { + availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + } + if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) { + availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + } + + var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY; + + if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) { + setPosition(node, node.layout.direction); } } diff --git a/src/__tests__/Layout-test.c b/src/__tests__/Layout-test.c index 13c449ea..7a9b913d 100644 --- a/src/__tests__/Layout-test.c +++ b/src/__tests__/Layout-test.c @@ -4070,6 +4070,7 @@ int main() node_1->context = "measureWithRatio2"; node_1 = node_0->get_child(node_0->context, 1); node_1->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_1->style.overflow = CSS_OVERFLOW_HIDDEN; node_1->style.dimensions[CSS_HEIGHT] = 100; init_css_node_children(node_1, 2); { @@ -4503,7 +4504,7 @@ int main() node_1 = node_0->get_child(node_0->context, 0); node_1->layout.position[CSS_TOP] = 20; node_1->layout.position[CSS_LEFT] = 20; - node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_WIDTH] = 60; node_1->layout.dimensions[CSS_HEIGHT] = 36; init_css_node_children(node_1, 1); { @@ -8004,6 +8005,1047 @@ int main() test("should center items correctly inside a stretched layout", root_node, root_layout); } + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 100; + node_2->style.dimensions[CSS_HEIGHT] = 25; + } + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 25; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 25; + } + } + } + + test("should not shrink column node when there is space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 100; + node_2->style.dimensions[CSS_HEIGHT] = 200; + } + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 200; + } + } + } + + test("should shrink column node when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 25; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 100; + node_2->style.dimensions[CSS_HEIGHT] = 30; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 15; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 25; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 25; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 30; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 30; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 55; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 15; + } + } + + test("should not shrink column node with siblings when there is space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 25; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 100; + node_2->style.dimensions[CSS_HEIGHT] = 80; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 15; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 25; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 25; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 60; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 80; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 85; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 15; + } + } + + test("should shrink column node with siblings when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 30; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 40; + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 50; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 22.5; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 22.5; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 40; + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 62.5; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 37.5; + } + } + + test("should shrink column nodes proportional to their main size when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 25; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 25; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + } + } + + test("should not shrink visible row node when there is space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 200; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 200; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + } + } + + test("should shrink visible row node when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 25; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 30; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 15; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 25; + node_1->layout.dimensions[CSS_WIDTH] = 30; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 30; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 55; + node_1->layout.dimensions[CSS_WIDTH] = 15; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should not shrink visible row node with siblings when there is space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 25; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 80; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 15; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 25; + node_1->layout.dimensions[CSS_WIDTH] = 60; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 80; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 85; + node_1->layout.dimensions[CSS_WIDTH] = 15; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should shrink visible row node with siblings when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 30; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.dimensions[CSS_WIDTH] = 40; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 22.5; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 22.5; + node_1->layout.dimensions[CSS_WIDTH] = 40; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 62.5; + node_1->layout.dimensions[CSS_WIDTH] = 37.5; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should shrink visible row nodes when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.overflow = CSS_OVERFLOW_HIDDEN; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 25; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 25; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + } + } + + test("should not shrink hidden row node when there is space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.overflow = CSS_OVERFLOW_HIDDEN; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 200; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 200; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + } + } + + test("should shrink hidden row node when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 25; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.overflow = CSS_OVERFLOW_HIDDEN; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 30; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 15; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 25; + node_1->layout.dimensions[CSS_WIDTH] = 30; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 30; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 55; + node_1->layout.dimensions[CSS_WIDTH] = 15; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should not shrink hidden row node with siblings when there is space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 25; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.overflow = CSS_OVERFLOW_HIDDEN; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.dimensions[CSS_WIDTH] = 80; + node_2->style.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 15; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 25; + node_1->layout.dimensions[CSS_WIDTH] = 60; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 80; + node_2->layout.dimensions[CSS_HEIGHT] = 100; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 85; + node_1->layout.dimensions[CSS_WIDTH] = 15; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should shrink hidden row node with siblings when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 100; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.overflow = CSS_OVERFLOW_HIDDEN; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 30; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.dimensions[CSS_WIDTH] = 40; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.overflow = CSS_OVERFLOW_HIDDEN; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_WIDTH] = 50; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 100; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 22.5; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 22.5; + node_1->layout.dimensions[CSS_WIDTH] = 40; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 62.5; + node_1->layout.dimensions[CSS_WIDTH] = 37.5; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should shrink hidden row nodes proportional to their main size when there is not any space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 213; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 25; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_1->style.align_items = CSS_ALIGN_FLEX_START; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->measure = measure; + node_2->context = "loooooooooong with space"; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 15; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 213; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 25; + node_1->layout.dimensions[CSS_WIDTH] = 172; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 172; + node_2->layout.dimensions[CSS_HEIGHT] = 18; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 197; + node_1->layout.dimensions[CSS_WIDTH] = 15; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should not shrink text node with siblings when there is space left over", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 140; + node_0->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.dimensions[CSS_WIDTH] = 25; + node_1->style.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_1->style.align_items = CSS_ALIGN_FLEX_START; + node_1->style.flex = -1; + node_1->style.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->style.flex = -1; + node_2->measure = measure; + node_2->context = "loooooooooong with space"; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->style.dimensions[CSS_WIDTH] = 15; + node_1->style.dimensions[CSS_HEIGHT] = 100; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 140; + node_0->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_0, 3); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 25; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 25; + node_1->layout.dimensions[CSS_WIDTH] = 100; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + init_css_node_children(node_1, 1); + { + css_node_t *node_2; + node_2 = node_1->get_child(node_1->context, 0); + node_2->layout.position[CSS_TOP] = 0; + node_2->layout.position[CSS_LEFT] = 0; + node_2->layout.dimensions[CSS_WIDTH] = 100; + node_2->layout.dimensions[CSS_HEIGHT] = 36; + } + node_1 = node_0->get_child(node_0->context, 2); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 125; + node_1->layout.dimensions[CSS_WIDTH] = 15; + node_1->layout.dimensions[CSS_HEIGHT] = 100; + } + } + + test("should shrink text node with siblings when there is not any space left over", root_node, root_layout); + } + { css_node_t *root_node = new_test_css_node(); { diff --git a/src/__tests__/Layout-test.js b/src/__tests__/Layout-test.js index a70ce673..d7edb768 100755 --- a/src/__tests__/Layout-test.js +++ b/src/__tests__/Layout-test.js @@ -1238,7 +1238,7 @@ describe('Layout', function() { testLayoutAgainstExpectedOnly( {style: {width: 320, flexDirection: 'column'}, children: [ {style: {measure: measureWithRatio2}}, - {style: {height: 100, flexDirection: 'row'}, children: [ + {style: {height: 100, flexDirection: 'row', overflow: 'hidden'}, children: [ {style: {measure: measureWithRatio2}}, {style: {measure: measureWithRatio2}} ]}, @@ -1354,14 +1354,16 @@ describe('Layout', function() { }); it('should layout node with text bounded by grand-parent', function() { - testLayout( + testLayoutAgainstExpectedOnly( {style: {width: 100, padding: 10, alignSelf: 'flex-start'}, children: [ {style: {margin: 10, alignSelf: 'flex-start'}, children: [ {style: {measure: text(texts.big)}} ]} ]}, {width: 100, height: 40 + textSizes.bigHeight, top: 0, left: 0, children: [ - {width: textSizes.bigMinWidth, height: textSizes.bigHeight, top: 20, left: 20, children: [ + // In the flexbox engine implementation, min width of text is not supported so we max + // out at the amount of available space (60) + {width: Math.min(60, textSizes.bigMinWidth), height: textSizes.bigHeight, top: 20, left: 20, children: [ {width: textSizes.bigMinWidth, height: textSizes.bigHeight, top: 0, left: 0} ]} ]} @@ -2485,6 +2487,307 @@ describe('Layout', function() { }); }); +describe('Layout flex:-1', function() { + // Tests for items with flex:-1 in a container with flexDirection:column + + it('should not shrink column node when there is space left over', function() { + testLayout( + {style: {width: 100, height: 100}, children: [ + {style: {width: 100, flex: -1}, children: [ + {style: {width: 100, height: 25}} + ]} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 25, top: 0, left: 0, children: [ + {width: 100, height: 25, top: 0, left: 0} + ]} + ]} + ); + }); + + it('should shrink column node when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100}, children: [ + {style: {width: 100, flex: -1}, children: [ + {style: {width: 100, height: 200}} + ]} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 200, top: 0, left: 0} + ]} + ]} + ); + }); + + it('should not shrink column node with siblings when there is space left over', function() { + testLayout( + {style: {width: 100, height: 100}, children: [ + {style: {width: 100, height: 25}}, + {style: {width: 100, flex: -1}, children: [ + {style: {width: 100, height: 30}} + ]}, + {style: {width: 100, height: 15}}, + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 25, top: 0, left: 0}, + {width: 100, height: 30, top: 25, left: 0, children: [ + {width: 100, height: 30, top: 0, left: 0} + ]}, + {width: 100, height: 15, top: 55, left: 0}, + ]} + ); + }); + + it('should shrink column node with siblings when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100}, children: [ + {style: {width: 100, height: 25}}, + {style: {width: 100, flex: -1}, children: [ + {style: {width: 100, height: 80}} + ]}, + {style: {width: 100, height: 15}}, + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 25, top: 0, left: 0}, + {width: 100, height: 60, top: 25, left: 0, children: [ + {width: 100, height: 80, top: 0, left: 0} + ]}, + {width: 100, height: 15, top: 85, left: 0}, + ]} + ); + }); + + it('should shrink column nodes proportional to their main size when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100}, children: [ + {style: {width: 100, height: 30, flex: -1}}, + {style: {width: 100, height: 40}}, + {style: {width: 100, height: 50, flex: -1}} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 22.5, top: 0, left: 0}, + {width: 100, height: 40, top: 22.5, left: 0}, + {width: 100, height: 37.5, top: 62.5, left: 0} + ]} + ); + }); + + // Tests for items with flex:-1 and overflow:visible in a container with flexDirection:row + + it('should not shrink visible row node when there is space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {height: 100, flex: -1}, children: [ + {style: {width: 25, height: 100}} + ]} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0} + ]} + ]} + ); + }); + + it('should shrink visible row node when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {height: 100, flex: -1}, children: [ + {style: {width: 200, height: 100}} + ]} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + // width would be 100 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0 + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 200, height: 100, top: 0, left: 0} + ]} + ]} + ); + }); + + it('should not shrink visible row node with siblings when there is space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 25, height: 100}}, + {style: {height: 100, flex: -1}, children: [ + {style: {width: 30, height: 100}} + ]}, + {style: {width: 15, height: 100}}, + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0}, + {width: 30, height: 100, top: 0, left: 25, children: [ + {width: 30, height: 100, top: 0, left: 0} + ]}, + {width: 15, height: 100, top: 0, left: 55}, + ]} + ); + }); + + it('should shrink visible row node with siblings when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 25, height: 100}}, + {style: {height: 100, flex: -1}, children: [ + {style: {width: 80, height: 100}} + ]}, + {style: {width: 15, height: 100}}, + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0}, + // width would be 80 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0 + {width: 60, height: 100, top: 0, left: 25, children: [ + {width: 80, height: 100, top: 0, left: 0} + ]}, + {width: 15, height: 100, top: 0, left: 85}, + ]} + ); + }); + + it('should shrink visible row nodes when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 30, height: 100, flex: -1}}, + {style: {width: 40, height: 100}}, + {style: {width: 50, height: 100, flex: -1}} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + // width would be 30 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0 + {width: 22.5, height: 100, top: 0, left: 0}, + {width: 40, height: 100, top: 0, left: 22.5}, + // width would be 50 if we implemented https://www.w3.org/TR/css-flexbox-1/#min-size-auto and min-width didn't default to 0 + {width: 37.5, height: 100, top: 0, left: 62.5} + ]} + ); + }); + + // Tests for items with flex:-1 and overflow:hidden in a container with flexDirection:row + + it('should not shrink hidden row node when there is space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {height: 100, flex: -1, overflow: 'hidden'}, children: [ + {style: {width: 25, height: 100}} + ]} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0} + ]} + ]} + ); + }); + + it('should shrink hidden row node when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {height: 100, flex: -1, overflow: 'hidden'}, children: [ + {style: {width: 200, height: 100}} + ]} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 200, height: 100, top: 0, left: 0} + ]} + ]} + ); + }); + + it('should not shrink hidden row node with siblings when there is space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 25, height: 100}}, + {style: {height: 100, flex: -1, overflow: 'hidden'}, children: [ + {style: {width: 30, height: 100}} + ]}, + {style: {width: 15, height: 100}}, + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0}, + {width: 30, height: 100, top: 0, left: 25, children: [ + {width: 30, height: 100, top: 0, left: 0} + ]}, + {width: 15, height: 100, top: 0, left: 55}, + ]} + ); + }); + + it('should shrink hidden row node with siblings when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 25, height: 100}}, + {style: {height: 100, flex: -1, overflow: 'hidden'}, children: [ + {style: {width: 80, height: 100}} + ]}, + {style: {width: 15, height: 100}}, + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0}, + {width: 60, height: 100, top: 0, left: 25, children: [ + {width: 80, height: 100, top: 0, left: 0} + ]}, + {width: 15, height: 100, top: 0, left: 85}, + ]} + ); + }); + + it('should shrink hidden row nodes proportional to their main size when there is not any space left over', function() { + testLayout( + {style: {width: 100, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 30, height: 100, flex: -1, overflow: 'hidden'}}, + {style: {width: 40, height: 100}}, + {style: {width: 50, height: 100, flex: -1, overflow: 'hidden'}} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 22.5, height: 100, top: 0, left: 0}, + {width: 40, height: 100, top: 0, left: 22.5}, + {width: 37.5, height: 100, top: 0, left: 62.5} + ]} + ); + }); + + // Tests for items with flex:-1 containing a text node + + it('should not shrink text node with siblings when there is space left over', function() { + testLayoutAgainstExpectedOnly( + {style: {width: 213, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 25, height: 100}}, + {style: {height: 100, flex: -1, flexDirection: 'row', alignItems: 'flex-start'}, children: [ + {style: {measure: text(texts.big)}} + ]}, + {style: {width: 15, height: 100}}, + ]}, + {width: 213, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0}, + {width: textSizes.bigWidth, height: 100, top: 0, left: 25, children: [ + {width: textSizes.bigWidth, height: textSizes.smallHeight, top: 0, left: 0} + ]}, + {width: 15, height: 100, top: 0, left: 25 + textSizes.bigWidth}, + ]} + ); + }); + + it('should shrink text node with siblings when there is not any space left over', function() { + testLayout( + {style: {width: 140, height: 100, flexDirection: 'row'}, children: [ + {style: {width: 25, height: 100}}, + {style: {height: 100, flex: -1, flexDirection: 'row', alignItems: 'flex-start'}, children: [ + {style: {flex: -1, measure: text(texts.big)}} + ]}, + {style: {width: 15, height: 100}}, + ]}, + {width: 140, height: 100, top: 0, left: 0, children: [ + {width: 25, height: 100, top: 0, left: 0}, + {width: textSizes.bigMinWidth, height: 100, top: 0, left: 25, children: [ + {width: textSizes.bigMinWidth, height: textSizes.bigHeight, top: 0, left: 0} + ]}, + {width: 15, height: 100, top: 0, left: 25 + textSizes.bigMinWidth}, + ]} + ); + }); +}); + describe('Layout alignContent', function() { it('should layout with alignContent: stretch, and alignItems: flex-start', function() { diff --git a/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs b/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs index c44dd882..63fb0c9d 100644 --- a/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs +++ b/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs @@ -36,7 +36,7 @@ public class LayoutEngineTest Math.Min(width, TestConstants.SMALL_WIDTH), TestConstants.SMALL_HEIGHT); } else if (testNode.context.Equals(TestConstants.LONG_TEXT)) { - if (CSSConstants.IsUndefined(width)) { + if (widthMode == CSSMeasureMode.Undefined) { width = 10000000; } return new MeasureOutput(width >= TestConstants.BIG_WIDTH @@ -55,10 +55,10 @@ public class LayoutEngineTest } } else if (testNode.context.Equals(TestConstants.MEASURE_WITH_MATCH_PARENT)) { if (widthMode == CSSMeasureMode.Undefined) { - width = 99999; + width = 99999; } if (heightMode == CSSMeasureMode.Undefined) { - height = 99999; + height = 99999; } return new MeasureOutput(width, height); } else { @@ -4371,6 +4371,7 @@ public class LayoutEngineTest node_1.context = "measureWithRatio2"; node_1 = node_0.getChildAt(1); node_1.style.flexDirection = CSSFlexDirection.Row; + node_1.style.overflow = CSSOverflow.Hidden; node_1.style.dimensions[DIMENSION_HEIGHT] = 100; addChildren(node_1, 2); { @@ -4822,7 +4823,7 @@ public class LayoutEngineTest node_1 = node_0.getChildAt(0); node_1.layout.position[POSITION_TOP] = 20; node_1.layout.position[POSITION_LEFT] = 20; - node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_WIDTH] = 60; node_1.layout.dimensions[DIMENSION_HEIGHT] = 36; addChildren(node_1, 1); { @@ -8388,7 +8389,7 @@ public class LayoutEngineTest } } - test("should layout child whose cross axis is undefined and whose alignSelf is stretch", root_node, root_layout); + test("should layout child whose cross axis is null and whose alignSelf is stretch", root_node, root_layout); } [Test] @@ -8483,6 +8484,1081 @@ public class LayoutEngineTest [Test] public void TestCase188() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 25; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 25; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 25; + } + } + } + + test("should not shrink column node when there is space left over", root_node, root_layout); + } + + [Test] + public void TestCase189() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 200; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 200; + } + } + } + + test("should shrink column node when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase190() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 30; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 25; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 30; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 30; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 55; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + test("should not shrink column node with siblings when there is space left over", root_node, root_layout); + } + + [Test] + public void TestCase191() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 80; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 25; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 60; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 80; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 85; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + test("should shrink column node with siblings when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase192() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 30; + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 40; + node_1 = node_0.getChildAt(2); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 50; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 22.5f; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 22.5f; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 40; + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 62.5f; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 37.5f; + } + } + + test("should shrink column nodes proportional to their main size when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase193() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 25; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 25; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should not shrink visible row node when there is space left over", root_node, root_layout); + } + + [Test] + public void TestCase194() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 200; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 200; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should shrink visible row node when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase195() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 30; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 30; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 30; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 55; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should not shrink visible row node with siblings when there is space left over", root_node, root_layout); + } + + [Test] + public void TestCase196() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 80; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 60; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 80; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 85; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink visible row node with siblings when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase197() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 30; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 40; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 50; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 22.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 22.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 40; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 62.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 37.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink visible row nodes when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase198() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.overflow = CSSOverflow.Hidden; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 25; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 25; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should not shrink hidden row node when there is space left over", root_node, root_layout); + } + + [Test] + public void TestCase199() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.overflow = CSSOverflow.Hidden; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 200; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 200; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should shrink hidden row node when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase200() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.overflow = CSSOverflow.Hidden; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 30; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 30; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 30; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 55; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should not shrink hidden row node with siblings when there is space left over", root_node, root_layout); + } + + [Test] + public void TestCase201() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.overflow = CSSOverflow.Hidden; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 80; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 60; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 80; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 85; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink hidden row node with siblings when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase202() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.overflow = CSSOverflow.Hidden; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 30; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 40; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.style.overflow = CSSOverflow.Hidden; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 50; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 22.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 22.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 40; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 62.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 37.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink hidden row nodes proportional to their main size when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase203() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 213; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flexDirection = CSSFlexDirection.Row; + node_1.style.alignItems = CSSAlign.FlexStart; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.setMeasureFunction(sTestMeasureFunction); + node_2.context = "loooooooooong with space"; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 213; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 172; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 172; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 18; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 197; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should not shrink text node with siblings when there is space left over", root_node, root_layout); + } + + [Test] + public void TestCase204() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 140; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flexDirection = CSSFlexDirection.Row; + node_1.style.alignItems = CSSAlign.FlexStart; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.flex = -1; + node_2.setMeasureFunction(sTestMeasureFunction); + node_2.context = "loooooooooong with space"; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 140; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 36; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 125; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink text node with siblings when there is not any space left over", root_node, root_layout); + } + + [Test] + public void TestCase205() { TestCSSNode root_node = new TestCSSNode(); { diff --git a/src/csharp/Facebook.CSSLayout/Assertions.cs b/src/csharp/Facebook.CSSLayout/Assertions.cs index 61e88490..1bede13e 100755 --- a/src/csharp/Facebook.CSSLayout/Assertions.cs +++ b/src/csharp/Facebook.CSSLayout/Assertions.cs @@ -18,5 +18,10 @@ namespace Facebook.CSSLayout Debug.Assert(v != null); return v; } + + public static void assertCondition(bool condition, string explanation) + { + Debug.Assert(condition, explanation); + } } } diff --git a/src/csharp/Facebook.CSSLayout/CSSCachedMeasurement.cs b/src/csharp/Facebook.CSSLayout/CSSCachedMeasurement.cs new file mode 100644 index 00000000..8eabe5d3 --- /dev/null +++ b/src/csharp/Facebook.CSSLayout/CSSCachedMeasurement.cs @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +namespace Facebook.CSSLayout +{ + sealed class CSSCachedMeasurement + { + public float availableWidth; + public float availableHeight; + public CSSMeasureMode? widthMeasureMode = null; + public CSSMeasureMode? heightMeasureMode = null; + + public float computedWidth; + public float computedHeight; + } +} diff --git a/src/csharp/Facebook.CSSLayout/CSSLayout.cs b/src/csharp/Facebook.CSSLayout/CSSLayout.cs index 8cba7126..e3b8f621 100644 --- a/src/csharp/Facebook.CSSLayout/CSSLayout.cs +++ b/src/csharp/Facebook.CSSLayout/CSSLayout.cs @@ -16,6 +16,10 @@ namespace Facebook.CSSLayout class CSSLayout { + // This value was chosen based on empiracle data. Even the most complicated + // layouts should not require more than 16 entries to fit within the cache. + public const int MAX_CACHED_RESULT_COUNT = 16; + public const int POSITION_LEFT = 0; public const int POSITION_TOP = 1; public const int POSITION_RIGHT = 2; @@ -25,12 +29,25 @@ namespace Facebook.CSSLayout public const int DIMENSION_HEIGHT = 1; public float[] position = new float[4]; - public float[] dimensions = new float[2]; + public float[] dimensions = { + CSSConstants.Undefined, + CSSConstants.Undefined + }; public CSSDirection direction = CSSDirection.LTR; - /** - * This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)} - */ + public float flexBasis; + + public int generationCount; + public CSSDirection? lastParentDirection; + + public int nextCachedMeasurementsIndex; + public CSSCachedMeasurement[] cachedMeasurements = new CSSCachedMeasurement[MAX_CACHED_RESULT_COUNT]; + public float[] measuredDimensions = { + CSSConstants.Undefined, + CSSConstants.Undefined + }; + + public CSSCachedMeasurement cachedLayout = new CSSCachedMeasurement(); public void resetResult() { @@ -38,17 +55,18 @@ namespace Facebook.CSSLayout FillArray(dimensions, CSSConstants.Undefined); direction = CSSDirection.LTR; - } - public void copy(CSSLayout layout) - { - position[POSITION_LEFT] = layout.position[POSITION_LEFT]; - position[POSITION_TOP] = layout.position[POSITION_TOP]; - position[POSITION_RIGHT] = layout.position[POSITION_RIGHT]; - position[POSITION_BOTTOM] = layout.position[POSITION_BOTTOM]; - dimensions[DIMENSION_WIDTH] = layout.dimensions[DIMENSION_WIDTH]; - dimensions[DIMENSION_HEIGHT] = layout.dimensions[DIMENSION_HEIGHT]; - direction = layout.direction; + flexBasis = 0; + + generationCount = 0; + lastParentDirection = null; + + nextCachedMeasurementsIndex = 0; + measuredDimensions[DIMENSION_WIDTH] = CSSConstants.Undefined; + measuredDimensions[DIMENSION_HEIGHT] = CSSConstants.Undefined; + + cachedLayout.widthMeasureMode = null; + cachedLayout.heightMeasureMode = null; } public override string ToString() diff --git a/src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs b/src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs index 29d23a0f..91fd1bd3 100755 --- a/src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs +++ b/src/csharp/Facebook.CSSLayout/CSSLayoutContext.cs @@ -22,5 +22,6 @@ namespace Facebook.CSSLayout { /*package*/ public MeasureOutput measureOutput = new MeasureOutput(); + public int currentGenerationCount; } } diff --git a/src/csharp/Facebook.CSSLayout/CSSMeasureMode.cs b/src/csharp/Facebook.CSSLayout/CSSMeasureMode.cs index 3e8dc546..6f48f888 100644 --- a/src/csharp/Facebook.CSSLayout/CSSMeasureMode.cs +++ b/src/csharp/Facebook.CSSLayout/CSSMeasureMode.cs @@ -1,4 +1,4 @@ -/** +/** * Copyright (c) 2014, Facebook, Inc. * All rights reserved. * diff --git a/src/csharp/Facebook.CSSLayout/CSSNode.cs b/src/csharp/Facebook.CSSLayout/CSSNode.cs index bf42d689..736fa289 100644 --- a/src/csharp/Facebook.CSSLayout/CSSNode.cs +++ b/src/csharp/Facebook.CSSLayout/CSSNode.cs @@ -58,8 +58,7 @@ namespace Facebook.CSSLayout internal readonly CachedCSSLayout lastLayout = new CachedCSSLayout(); internal int lineIndex = 0; - internal /*package*/ CSSNode nextAbsoluteChild; - internal /*package*/ CSSNode nextFlexChild; + internal /*package*/ CSSNode nextChild; // 4 is kinda arbitrary, but the default of 10 seems really high for an average View. readonly List mChildren = new List(4); @@ -155,7 +154,6 @@ namespace Facebook.CSSLayout public void CalculateLayout() { - layout.resetResult(); LayoutEngine.layoutNode(DummyLayoutContext, this, CSSConstants.Undefined, CSSConstants.Undefined, null); } diff --git a/src/csharp/Facebook.CSSLayout/CSSOverflow.cs b/src/csharp/Facebook.CSSLayout/CSSOverflow.cs new file mode 100644 index 00000000..1a78f0cb --- /dev/null +++ b/src/csharp/Facebook.CSSLayout/CSSOverflow.cs @@ -0,0 +1,17 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +namespace Facebook.CSSLayout +{ + public enum CSSOverflow + { + Visible, + Hidden + } +} diff --git a/src/csharp/Facebook.CSSLayout/CSSStyle.cs b/src/csharp/Facebook.CSSLayout/CSSStyle.cs index e14aa010..d03f06bd 100644 --- a/src/csharp/Facebook.CSSLayout/CSSStyle.cs +++ b/src/csharp/Facebook.CSSLayout/CSSStyle.cs @@ -22,6 +22,7 @@ namespace Facebook.CSSLayout public CSSAlign alignSelf = CSSAlign.Auto; public CSSPositionType positionType = CSSPositionType.Relative; public CSSWrap flexWrap = CSSWrap.NoWrap; + public CSSOverflow overflow = CSSOverflow.Visible; public float flex; public Spacing margin = new Spacing(); diff --git a/src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj b/src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj index 0fb115e9..43c2579f 100755 --- a/src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj +++ b/src/csharp/Facebook.CSSLayout/Facebook.CSSLayout.csproj @@ -41,14 +41,16 @@ + - + + @@ -68,4 +70,4 @@ --> - + \ No newline at end of file diff --git a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs index 9f448a37..175ec525 100644 --- a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs +++ b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs @@ -19,6 +19,8 @@ namespace Facebook.CSSLayout static class LayoutEngine { + const boolean POSITIVE_FLEX_IS_AUTO = false; + const int POSITION_LEFT = CSSLayout.POSITION_LEFT; const int POSITION_TOP = CSSLayout.POSITION_TOP; const int POSITION_RIGHT = CSSLayout.POSITION_RIGHT; @@ -80,7 +82,52 @@ namespace Facebook.CSSLayout Spacing.END }; - private static float boundAxis(CSSNode node, int axis, float value) + private static boolean isFlexBasisAuto(CSSNode node) + { + if (POSITIVE_FLEX_IS_AUTO) + { + // All flex values are auto. + return true; + } + else + { + // A flex value > 0 implies a basis of zero. + return node.style.flex <= 0; + } + } + + private static float getFlexGrowFactor(CSSNode node) + { + // Flex grow is implied by positive values for flex. + if (node.style.flex > 0) + { + return node.style.flex; + } + return 0; + } + + private static float getFlexShrinkFactor(CSSNode node) + { + if (POSITIVE_FLEX_IS_AUTO) + { + // A flex shrink factor of 1 is implied by non-zero values for flex. + if (node.style.flex != 0) + { + return 1; + } + } + else + { + // A flex shrink factor of 1 is implied by negative values for flex. + if (node.style.flex < 0) + { + return 1; + } + } + return 0; + } + + private static float boundAxisWithinMinAndMax(CSSNode node, int axis, float value) { float min = CSSConstants.Undefined; float max = CSSConstants.Undefined; @@ -109,29 +156,15 @@ namespace Facebook.CSSLayout return boundValue; } - - private static void setDimensionFromStyle(CSSNode node, int axis) + + private static float boundAxis(CSSNode node, int axis, float value) { - // The parent already computed us a width or height. We just skip it - if (!float.IsNaN(node.layout.dimensions[dim[axis]])) - { - return; - } - // We only run if there's a width or height defined - if (float.IsNaN(node.style.dimensions[dim[axis]]) || - node.style.dimensions[dim[axis]] <= 0.0) - { - return; - } - - // The dimensions can never be smaller than the padding and border - float maxLayoutDimension = Math.Max( - boundAxis(node, axis, node.style.dimensions[dim[axis]]), + float paddingAndBorderAxis = node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + - node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + - node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])); - node.layout.dimensions[dim[axis]] = maxLayoutDimension; + node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]); + return Math.Max(boundAxisWithinMinAndMax(node, axis, value), paddingAndBorderAxis); } private static float getRelativePosition(CSSNode node, int axis) @@ -145,6 +178,21 @@ namespace Facebook.CSSLayout float trailingPos = node.style.position[trailing[axis]]; return float.IsNaN(trailingPos) ? 0 : -trailingPos; } + + private static void setPosition(CSSNode node, CSSDirection direction) + { + int mainAxis = resolveAxis(getFlexDirection(node), direction); + int crossAxis = getCrossFlexDirection(mainAxis, direction); + + node.layout.position[leading[mainAxis]] = node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + + getRelativePosition(node, mainAxis); + node.layout.position[trailing[mainAxis]] = node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + + getRelativePosition(node, mainAxis); + node.layout.position[leading[crossAxis]] = node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + + getRelativePosition(node, crossAxis); + node.layout.position[trailing[crossAxis]] = node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + + getRelativePosition(node, crossAxis); + } static int resolveAxis(int axis, CSSDirection direction) { @@ -205,394 +253,516 @@ namespace Facebook.CSSLayout return node.IsMeasureDefined; } - static boolean needsRelayout(CSSNode node, float parentMaxWidth, float parentMaxHeight) + internal static void layoutNode(CSSLayoutContext layoutContext, CSSNode node, float availableWidth, float availableHeight, CSSDirection? parentDirection) { - return node.isDirty() || - !FloatUtil.floatsEqual( - node.lastLayout.requestedHeight, - node.layout.dimensions[DIMENSION_HEIGHT]) || - !FloatUtil.floatsEqual( - node.lastLayout.requestedWidth, - node.layout.dimensions[DIMENSION_WIDTH]) || - !FloatUtil.floatsEqual(node.lastLayout.parentMaxWidth, parentMaxWidth) || - !FloatUtil.floatsEqual(node.lastLayout.parentMaxHeight, parentMaxHeight); + // Increment the generation count. This will force the recursive routine to visit + // all dirty nodes at least once. Subsequent visits will be skipped if the input + // parameters don't change. + layoutContext.currentGenerationCount++; + + // If the caller didn't specify a height/width, use the dimensions + // specified in the style. + if (float.IsNaN(availableWidth) && node.style.dimensions[DIMENSION_WIDTH] >= 0.0) + { + float marginAxisRow = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + availableWidth = node.style.dimensions[DIMENSION_WIDTH] + marginAxisRow; + } + if (float.IsNaN(availableHeight) && node.style.dimensions[DIMENSION_HEIGHT] >= 0.0) + { + float marginAxisColumn = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + availableHeight = node.style.dimensions[DIMENSION_HEIGHT] + marginAxisColumn; + } + + CSSMeasureMode widthMeasureMode = float.IsNaN(availableWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly; + CSSMeasureMode heightMeasureMode = float.IsNaN(availableHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly; + + if (layoutNodeInternal(layoutContext, node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, "initial")) + { + setPosition(node, node.layout.direction); + } } - internal static void layoutNode(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, float parentMaxHeight, CSSDirection? parentDirection) + // + // This is a wrapper around the layoutNodeImpl function. It determines + // whether the layout request is redundant and can be skipped. + // + // Parameters: + // Input parameters are the same as layoutNodeImpl (see below) + // Return parameter is true if layout was performed, false if skipped + // + internal static boolean layoutNodeInternal(CSSLayoutContext layoutContext, CSSNode node, float availableWidth, float availableHeight, CSSDirection? parentDirection, CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, boolean performLayout, string reason) { - if (needsRelayout(node, parentMaxWidth, parentMaxHeight)) - { - node.lastLayout.requestedWidth = node.layout.dimensions[DIMENSION_WIDTH]; - node.lastLayout.requestedHeight = node.layout.dimensions[DIMENSION_HEIGHT]; - node.lastLayout.parentMaxWidth = parentMaxWidth; - node.lastLayout.parentMaxHeight = parentMaxHeight; + CSSLayout layout = node.layout; - layoutNodeImpl(layoutContext, node, parentMaxWidth, parentMaxHeight, parentDirection); - node.lastLayout.copy(node.layout); + boolean needToVisitNode = (node.isDirty() && layout.generationCount != layoutContext.currentGenerationCount) || + layout.lastParentDirection != parentDirection; + + if (needToVisitNode) + { + // Invalidate the cached results. + layout.nextCachedMeasurementsIndex = 0; + layout.cachedLayout.widthMeasureMode = null; + layout.cachedLayout.heightMeasureMode = null; + } + + CSSCachedMeasurement cachedResults = null; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the positions + // and dimensions for nodes in the subtree. The algorithm assumes that each node + // gets layed out a maximum of one time per tree layout, but multiple measurements + // may be required to resolve all of the flex dimensions. + if (performLayout) + { + if (FloatUtil.floatsEqual(layout.cachedLayout.availableWidth, availableWidth) && + FloatUtil.floatsEqual(layout.cachedLayout.availableHeight, availableHeight) && + layout.cachedLayout.widthMeasureMode == widthMeasureMode && + layout.cachedLayout.heightMeasureMode == heightMeasureMode) + { + + cachedResults = layout.cachedLayout; + } } else { - node.layout.copy(node.lastLayout); + for (int i = 0; i < layout.nextCachedMeasurementsIndex; i++) + { + if (FloatUtil.floatsEqual(layout.cachedMeasurements[i].availableWidth, availableWidth) && + FloatUtil.floatsEqual(layout.cachedMeasurements[i].availableHeight, availableHeight) && + layout.cachedMeasurements[i].widthMeasureMode == widthMeasureMode && + layout.cachedMeasurements[i].heightMeasureMode == heightMeasureMode) + { + + cachedResults = layout.cachedMeasurements[i]; + break; + } + } } - node.markHasNewLayout(); - } - - static void layoutNodeImpl(CSSLayoutContext layoutContext, CSSNode node, float parentMaxWidth, float parentMaxHeight, CSSDirection? parentDirection) - { - var childCount_ = node.getChildCount(); - for (int i_ = 0; i_ < childCount_; i_++) + if (!needToVisitNode && cachedResults != null) { - node.getChildAt(i_).layout.resetResult(); + layout.measuredDimensions[DIMENSION_WIDTH] = cachedResults.computedWidth; + layout.measuredDimensions[DIMENSION_HEIGHT] = cachedResults.computedHeight; + } + else + { + layoutNodeImpl(layoutContext, node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout); + + layout.lastParentDirection = parentDirection; + + if (cachedResults == null) + { + if (layout.nextCachedMeasurementsIndex == CSSLayout.MAX_CACHED_RESULT_COUNT) + { + layout.nextCachedMeasurementsIndex = 0; + } + + CSSCachedMeasurement newCacheEntry = null; + if (performLayout) + { + // Use the single layout cache entry. + newCacheEntry = layout.cachedLayout; + } + else + { + // Allocate a new measurement cache entry. + newCacheEntry = layout.cachedMeasurements[layout.nextCachedMeasurementsIndex]; + if (newCacheEntry == null) + { + newCacheEntry = new CSSCachedMeasurement(); + layout.cachedMeasurements[layout.nextCachedMeasurementsIndex] = newCacheEntry; + } + layout.nextCachedMeasurementsIndex++; + } + + newCacheEntry.availableWidth = availableWidth; + newCacheEntry.availableHeight = availableHeight; + newCacheEntry.widthMeasureMode = widthMeasureMode; + newCacheEntry.heightMeasureMode = heightMeasureMode; + newCacheEntry.computedWidth = layout.measuredDimensions[DIMENSION_WIDTH]; + newCacheEntry.computedHeight = layout.measuredDimensions[DIMENSION_HEIGHT]; + } } + if (performLayout) + { + node.layout.dimensions[DIMENSION_WIDTH] = node.layout.measuredDimensions[DIMENSION_WIDTH]; + node.layout.dimensions[DIMENSION_HEIGHT] = node.layout.measuredDimensions[DIMENSION_HEIGHT]; + node.markHasNewLayout(); + } + layout.generationCount = layoutContext.currentGenerationCount; + return (needToVisitNode || cachedResults == null); + } + + // + // This is the main routine that implements a subset of the flexbox layout algorithm + // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/. + // + // Limitations of this algorithm, compared to the full standard: + // * Display property is always assumed to be 'flex' except for Text nodes, which + // are assumed to be 'inline-flex'. + // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are + // stacked in document order. + // * The 'order' property is not supported. The order of flex items is always defined + // by document order. + // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse' + // and 'hidden' are not supported. + // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The + // rarely-used 'wrap-reverse' is not supported. + // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and + // flexBasis, this algorithm supports only the three most common combinations: + // flex: 0 is equiavlent to flex: 0 0 auto + // flex: n (where n is a positive value) is equivalent to flex: n 1 auto + // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0 + // This is faster because the content doesn't need to be measured, but it's + // less flexible because the basis is always 0 and can't be overriden with + // the width/height attributes. + // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto + // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel + // values, and the default value is 0. + // * The 'baseline' value is not supported for alignItems and alignSelf properties. + // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be + // specified as pixel values, not as percentages. + // * There is no support for calculation of dimensions based on intrinsic aspect ratios + // (e.g. images). + // * There is no support for forced breaks. + // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text). + // + // Deviations from standard: + // * Section 4.5 of the spec indicates that all flex items have a default minimum + // main size. For text blocks, for example, this is the width of the widest word. + // Calculating the minimum width is expensive, so we forego it and assume a default + // minimum main size of 0. + // * Min/Max sizes in the main axis are not honored when resolving flexible lengths. + // * The spec indicates that the default value for 'flexDirection' is 'row', but + // the algorithm below assumes a default of 'column'. + // + // Input parameters: + // - node: current node to be sized and layed out + // - availableWidth & availableHeight: available size to be used for sizing the node + // or CSS_UNDEFINED if the size is not available; interpretation depends on layout + // flags + // - parentDirection: the inline (text) direction within the parent (left-to-right or + // right-to-left) + // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation) + // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation) + // - performLayout: specifies whether the caller is interested in just the dimensions + // of the node or it requires the entire node and its subtree to be layed out + // (with final positions) + // + // Details: + // This routine is called recursively to lay out subtrees of flexbox elements. It uses the + // information in node.style, which is treated as a read-only input. It is responsible for + // setting the layout.direction and layout.measured_dimensions fields for the input node as well + // as the layout.position and layout.line_index fields for its child nodes. The + // layout.measured_dimensions field includes any border or padding for the node but does + // not include margins. + // + // The spec describes four different layout modes: "fill available", "max content", "min content", + // and "fit content". Of these, we don't use "min content" because we don't support default + // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode + // from the spec (https://www.w3.org/TR/css3-sizing/#terms): + // - CSS_MEASURE_MODE_UNDEFINED: max content + // - CSS_MEASURE_MODE_EXACTLY: fill available + // - CSS_MEASURE_MODE_AT_MOST: fit content + // + // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of + // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension. + // + static void layoutNodeImpl(CSSLayoutContext layoutContext, CSSNode node, float availableWidth, float availableHeight, CSSDirection? parentDirection, CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, boolean performLayout) + { /** START_GENERATED **/ + Assertions.assertCondition(float.IsNaN(availableWidth) ? widthMeasureMode == CSSMeasureMode.Undefined : true, "availableWidth is indefinite so widthMeasureMode must be CSSMeasureMode.Undefined"); + Assertions.assertCondition(float.IsNaN(availableHeight) ? heightMeasureMode == CSSMeasureMode.Undefined : true, "availableHeight is indefinite so heightMeasureMode must be CSSMeasureMode.Undefined"); + + float paddingAndBorderAxisRow = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]))); + float paddingAndBorderAxisColumn = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]))); + float marginAxisRow = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + float marginAxisColumn = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + + // Set the resolved resolution in the node's layout. CSSDirection direction = resolveDirection(node, parentDirection); - int mainAxis = resolveAxis(getFlexDirection(node), direction); - int crossAxis = getCrossFlexDirection(mainAxis, direction); - int resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); - - // Handle width and height style attributes - setDimensionFromStyle(node, mainAxis); - setDimensionFromStyle(node, crossAxis); - - // Set the resolved resolution in the node's layout node.layout.direction = direction; - // The position is set by the parent, but we need to complete it with a - // delta composed of the margin and left/top/right/bottom - node.layout.position[leading[mainAxis]] += node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + - getRelativePosition(node, mainAxis); - node.layout.position[trailing[mainAxis]] += node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + - getRelativePosition(node, mainAxis); - node.layout.position[leading[crossAxis]] += node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + - getRelativePosition(node, crossAxis); - node.layout.position[trailing[crossAxis]] += node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + - getRelativePosition(node, crossAxis); - - // Inline immutable values from the target node to avoid excessive method - // invocations during the layout calculation. - int childCount = node.getChildCount(); - float paddingAndBorderAxisResolvedRow = ((node.style.padding.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.border.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis])) + (node.style.padding.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]) + node.style.border.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]))); - float paddingAndBorderAxisColumn = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]))); - + // For content (text) nodes, determine the dimensions based on the text contents. if (isMeasureDefined(node)) { - boolean isResolvedRowDimDefined = (!float.IsNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0); + float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + + if (widthMeasureMode == CSSMeasureMode.Exactly && heightMeasureMode == CSSMeasureMode.Exactly) { - float width = CSSConstants.Undefined; - CSSMeasureMode widthMode = CSSMeasureMode.Undefined; - if ((!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) { - width = node.style.dimensions[DIMENSION_WIDTH]; - widthMode = CSSMeasureMode.Exactly; - } else if (isResolvedRowDimDefined) { - width = node.layout.dimensions[dim[resolvedRowAxis]]; - widthMode = CSSMeasureMode.Exactly; + // Don't bother sizing the text if both dimensions are already defined. + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); + } else if (innerWidth <= 0) { + + // Don't bother sizing the text if there's no horizontal space. + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { - width = parentMaxWidth - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])); - widthMode = CSSMeasureMode.AtMost; - } - width -= paddingAndBorderAxisResolvedRow; - if (float.IsNaN(width)) { - widthMode = CSSMeasureMode.Undefined; - } - float height = CSSConstants.Undefined; - CSSMeasureMode heightMode = CSSMeasureMode.Undefined; - if ((!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - height = node.style.dimensions[DIMENSION_HEIGHT]; - heightMode = CSSMeasureMode.Exactly; - } else if ((!float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - height = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]; - heightMode = CSSMeasureMode.Exactly; - } else { - height = parentMaxHeight - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])); - heightMode = CSSMeasureMode.AtMost; - } - height -= ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]))); - if (float.IsNaN(height)) { - heightMode = CSSMeasureMode.Undefined; - } - - // We only need to give a dimension for the text if we haven't got any - // for it computed yet. It can either be from the style attribute or because - // the element is flexible. - boolean isRowUndefined = !(!float.IsNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0) && !isResolvedRowDimDefined; - boolean isColumnUndefined = !(!float.IsNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) && - float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); - - // Let's not measure the text if we already know both dimensions - if (isRowUndefined || isColumnUndefined) { + // Measure the text under the current constraints. MeasureOutput measureDim = node.measure( layoutContext.measureOutput, - width, - widthMode, - height, - heightMode + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode ); - if (isRowUndefined) { - node.layout.dimensions[DIMENSION_WIDTH] = measureDim.width + - paddingAndBorderAxisResolvedRow; - } - if (isColumnUndefined) { - node.layout.dimensions[DIMENSION_HEIGHT] = measureDim.height + - paddingAndBorderAxisColumn; - } + + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSSMeasureMode.Undefined || widthMeasureMode == CSSMeasureMode.AtMost) ? + measureDim.width + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSSMeasureMode.Undefined || heightMeasureMode == CSSMeasureMode.AtMost) ? + measureDim.height + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); } - if (childCount == 0) { + + return; + } + + // For nodes with no children, use the available values if they were provided, or + // the minimum size as indicated by the padding and border sizes. + int childCount = node.getChildCount(); + if (childCount == 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSSMeasureMode.Undefined || widthMeasureMode == CSSMeasureMode.AtMost) ? + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSSMeasureMode.Undefined || heightMeasureMode == CSSMeasureMode.AtMost) ? + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); + return; + } + + // If we're not being asked to perform a full layout, we can handle a number of common + // cases here without incurring the cost of the remaining function. + if (!performLayout) { + // If we're being asked to size the content with an at most constraint but there is no available width, + // the measurement will always be zero. + if (widthMeasureMode == CSSMeasureMode.AtMost && availableWidth <= 0 && + heightMeasureMode == CSSMeasureMode.AtMost && availableHeight <= 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + if (widthMeasureMode == CSSMeasureMode.AtMost && availableWidth <= 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, float.IsNaN(availableHeight) ? 0 : (availableHeight - marginAxisColumn)); + return; + } + + if (heightMeasureMode == CSSMeasureMode.AtMost && availableHeight <= 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, float.IsNaN(availableWidth) ? 0 : (availableWidth - marginAxisRow)); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + // If we're being asked to use an exact width/height, there's no need to measure the children. + if (widthMeasureMode == CSSMeasureMode.Exactly && heightMeasureMode == CSSMeasureMode.Exactly) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); return; } } - boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.Wrap); - + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + int mainAxis = resolveAxis(getFlexDirection(node), direction); + int crossAxis = getCrossFlexDirection(mainAxis, direction); + boolean isMainAxisRow = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE); CSSJustify justifyContent = node.style.justifyContent; - - float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])); - float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])); - float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))); - float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); - - boolean isMainDimDefined = (!float.IsNaN(node.layout.dimensions[dim[mainAxis]]) && node.layout.dimensions[dim[mainAxis]] >= 0.0); - boolean isCrossDimDefined = (!float.IsNaN(node.layout.dimensions[dim[crossAxis]]) && node.layout.dimensions[dim[crossAxis]] >= 0.0); - boolean isMainRowDirection = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE); - - int i; - int ii; - CSSNode child; - int axis; + boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.Wrap); CSSNode firstAbsoluteChild = null; CSSNode currentAbsoluteChild = null; - float definedMainDim = CSSConstants.Undefined; - if (isMainDimDefined) { - definedMainDim = node.layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; + float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])); + float trailingPaddingAndBorderMain = (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); + float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])); + float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))); + float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); + + CSSMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + CSSMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; + float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + CSSNode child; + int i; + float childWidth; + float childHeight; + CSSMeasureMode childWidthMeasureMode; + CSSMeasureMode childHeightMeasureMode; + for (i = 0; i < childCount; i++) { + child = node.getChildAt(i); + + if (performLayout) { + // Set the initial position (relative to the parent). + CSSDirection childDirection = resolveDirection(child, direction); + setPosition(child, childDirection); + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if (child.style.positionType == CSSPositionType.Absolute) { + + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild == null) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild != null) { + currentAbsoluteChild.nextChild = child; + } + currentAbsoluteChild = child; + child.nextChild = null; + } else { + + if (isMainAxisRow && (child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + + // The width is definite, so use that as the flex basis. + child.layout.flexBasis = Math.Max(child.style.dimensions[DIMENSION_WIDTH], ((child.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW])) + (child.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]) + child.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])))); + } else if (!isMainAxisRow && (child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + + // The height is definite, so use that as the flex basis. + child.layout.flexBasis = Math.Max(child.style.dimensions[DIMENSION_HEIGHT], ((child.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (child.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + child.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])))); + } else if (!isFlexBasisAuto(child) && !float.IsNaN(availableInnerMainDim)) { + + // If the basis isn't 'auto', it is assumed to be zero. + child.layout.flexBasis = Math.Max(0, ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))); + } else { + + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). + childWidth = CSSConstants.Undefined; + childHeight = CSSConstants.Undefined; + childWidthMeasureMode = CSSMeasureMode.Undefined; + childHeightMeasureMode = CSSMeasureMode.Undefined; + + if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = child.style.dimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.Exactly; + } + if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = child.style.dimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.Exactly; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && float.IsNaN(childWidth) && !float.IsNaN(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSSMeasureMode.AtMost; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node.style.overflow == CSSOverflow.Hidden) { + if (isMainAxisRow && float.IsNaN(childHeight) && !float.IsNaN(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSSMeasureMode.AtMost; + } + } + + // Measure the child + layoutNodeInternal(layoutContext, child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "measure"); + + child.layout.flexBasis = Math.Max(isMainAxisRow ? child.layout.measuredDimensions[DIMENSION_WIDTH] : child.layout.measuredDimensions[DIMENSION_HEIGHT], ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))); + } + } } - // We want to execute the next two loops one per line with flex-wrap - int startLine = 0; - int endLine = 0; - // int nextOffset = 0; - int alreadyComputedNextLayout = 0; - // We aggregate the total dimensions of the container in those two variables - float linesCrossDim = 0; - float linesMainDim = 0; - int linesCount = 0; - while (endLine < childCount) { - // Layout non flexible children and count children by type + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + int startOfLineIndex = 0; + int endOfLineIndex = 0; + + // Number of lines. + int lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; - // mainContentDim is accumulation of the dimensions and margin of all the - // non flexible children. This will be used in order to either set the - // dimensions of the node if none already exist, or to compute the - // remaining space left for the flexible children. - float mainContentDim = 0; + // Max main dimension of all the lines. + float maxLineMainDim = 0; - // There are three kind of children, non flexible, flexible and absolute. - // We need to know how many there are in order to distribute the space. - int flexibleChildrenCount = 0; - float totalFlexible = 0; - int nonFlexibleChildrenCount = 0; + while (endOfLineIndex < childCount) { + + // Number of items on the currently line. May be different than the difference + // between start and end indicates because we skip over absolute-positioned items. + int itemsOnLine = 0; - // Use the line loop to position children in the main axis for as long - // as they are using a simple stacking behaviour. Children that are - // immediately stacked in the initial loop will not be touched again - // in . - boolean isSimpleStackMain = - (isMainDimDefined && justifyContent == CSSJustify.FlexStart) || - (!isMainDimDefined && justifyContent != CSSJustify.Center); - int firstComplexMain = (isSimpleStackMain ? childCount : startLine); + // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin + // of all the children on the current line. This will be used in order to + // either set the dimensions of the node if none already exist or to compute + // the remaining space left for the flexible children. + float sizeConsumedOnCurrentLine = 0; - // Use the initial line loop to position children in the cross axis for - // as long as they are relatively positioned with alignment STRETCH or - // FLEX_START. Children that are immediately stacked in the initial loop - // will not be touched again in . - boolean isSimpleStackCross = true; - int firstComplexCross = childCount; + float totalFlexGrowFactors = 0; + float totalFlexShrinkScaledFactors = 0; - CSSNode firstFlexChild = null; - CSSNode currentFlexChild = null; + i = startOfLineIndex; - float mainDim = leadingPaddingAndBorderMain; - float crossDim = 0; + // Maintain a linked list of the child nodes that can shrink and/or grow. + CSSNode firstRelativeChild = null; + CSSNode currentRelativeChild = null; - float maxWidth = CSSConstants.Undefined; - float maxHeight = CSSConstants.Undefined; - for (i = startLine; i < childCount; ++i) { + // Add items to the current line until it's full or we run out of items. + while (i < childCount) { child = node.getChildAt(i); - child.lineIndex = linesCount; + child.lineIndex = lineCount; - child.nextAbsoluteChild = null; - child.nextFlexChild = null; - - CSSAlign alignItem = getAlignItem(node, child); - - // Pre-fill cross axis dimensions when the child is using stretch before - // we call the recursive layout pass - if (alignItem == CSSAlign.Stretch && - child.style.positionType == CSSPositionType.Relative && - isCrossDimDefined && - !(!float.IsNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] >= 0.0)) { - child.layout.dimensions[dim[crossAxis]] = Math.Max( - boundAxis(child, crossAxis, node.layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))), - // You never want to go smaller than padding - ((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))) - ); - } else if (child.style.positionType == CSSPositionType.Absolute) { - // Store a private linked list of absolutely positioned children - // so that we can efficiently traverse them later. - if (firstAbsoluteChild == null) { - firstAbsoluteChild = child; + if (child.style.positionType != CSSPositionType.Absolute) { + float outerFlexBasis = child.layout.flexBasis + (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); + + // If this is a multi-line flow and this item pushes us over the available size, we've + // hit the end of the current line. Break out of the loop and lay out the current line. + if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) { + break; } - if (currentAbsoluteChild != null) { - currentAbsoluteChild.nextAbsoluteChild = child; - } - currentAbsoluteChild = child; - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if ((!float.IsNaN(node.layout.dimensions[dim[axis]]) && node.layout.dimensions[dim[axis]] >= 0.0) && - !(!float.IsNaN(child.style.dimensions[dim[axis]]) && child.style.dimensions[dim[axis]] >= 0.0) && - !float.IsNaN(child.style.position[leading[axis]]) && - !float.IsNaN(child.style.position[trailing[axis]])) { - child.layout.dimensions[dim[axis]] = Math.Max( - boundAxis(child, axis, node.layout.dimensions[dim[axis]] - - ((node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) - - (child.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) - - (float.IsNaN(child.style.position[leading[axis]]) ? 0 : child.style.position[leading[axis]]) - - (float.IsNaN(child.style.position[trailing[axis]]) ? 0 : child.style.position[trailing[axis]])), - // You never want to go smaller than padding - ((child.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (child.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + child.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) - ); - } + sizeConsumedOnCurrentLine += outerFlexBasis; + itemsOnLine++; + + if ((child.style.positionType == CSSPositionType.Relative && child.style.flex != 0)) { + totalFlexGrowFactors += getFlexGrowFactor(child); + + // Unlike the grow factor, the shrink factor is scaled relative to the child + // dimension. + totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis; } + + // Store a private linked list of children that need to be layed out. + if (firstRelativeChild == null) { + firstRelativeChild = child; + } + if (currentRelativeChild != null) { + currentRelativeChild.nextChild = child; + } + currentRelativeChild = child; + child.nextChild = null; } - - float nextContentDim = 0; - - // It only makes sense to consider a child flexible if we have a computed - // dimension for the node. - if (isMainDimDefined && (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0)) { - flexibleChildrenCount++; - totalFlexible += child.style.flex; - - // Store a private linked list of flexible children so that we can - // efficiently traverse them later. - if (firstFlexChild == null) { - firstFlexChild = child; - } - if (currentFlexChild != null) { - currentFlexChild.nextFlexChild = child; - } - currentFlexChild = child; - - // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information, which represents - // the smallest possible size for the child, to compute the remaining - // available space. - nextContentDim = ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) + - (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - - } else { - maxWidth = CSSConstants.Undefined; - maxHeight = CSSConstants.Undefined; - - if (!isMainRowDirection) { - if ((!float.IsNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0)) { - maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else { - maxWidth = parentMaxWidth - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) - - paddingAndBorderAxisResolvedRow; - } - } else { - if ((!float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else { - maxHeight = parentMaxHeight - - (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) - - paddingAndBorderAxisColumn; - } - } - - // This is the main recursive call. We layout non flexible children. - if (alreadyComputedNextLayout == 0) { - layoutNode(layoutContext, child, maxWidth, maxHeight, direction); - } - - // Absolute positioned elements do not take part of the layout, so we - // don't use them to compute mainContentDim - if (child.style.positionType == CSSPositionType.Relative) { - nonFlexibleChildrenCount++; - // At this point we know the final size and margin of the element. - nextContentDim = (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - } - } - - // The element we are about to add would make us go to the next line - if (isNodeFlexWrap && - isMainDimDefined && - mainContentDim + nextContentDim > definedMainDim && - // If there's only one element, then it's bigger than the content - // and needs its own line - i != startLine) { - nonFlexibleChildrenCount--; - alreadyComputedNextLayout = 1; - break; - } - - // Disable simple stacking in the main axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackMain && - (child.style.positionType != CSSPositionType.Relative || (child.style.positionType == CSSPositionType.Relative && child.style.flex > 0))) { - isSimpleStackMain = false; - firstComplexMain = i; - } - - // Disable simple stacking in the cross axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackCross && - (child.style.positionType != CSSPositionType.Relative || - (alignItem != CSSAlign.Stretch && alignItem != CSSAlign.FlexStart) || - (alignItem == CSSAlign.Stretch && !isCrossDimDefined))) { - isSimpleStackCross = false; - firstComplexCross = i; - } - - if (isSimpleStackMain) { - child.layout.position[pos[mainAxis]] += mainDim; - if (isMainDimDefined) { - child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]]; - } - - mainDim += (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - crossDim = Math.Max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))); - } - - if (isSimpleStackCross) { - child.layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; - if (isCrossDimDefined) { - child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]]; - } - } - - alreadyComputedNextLayout = 0; - mainContentDim += nextContentDim; - endLine = i + 1; + + i++; + endOfLineIndex++; } - - // Layout flexible children and allocate empty space + + // If we don't need to measure the cross axis, we can skip the entire flex step. + boolean canSkipFlex = !performLayout && measureModeCrossDim == CSSMeasureMode.Exactly; // In order to position the elements in the main axis, we have two // controls. The space between the beginning and the first element @@ -600,212 +770,300 @@ namespace Facebook.CSSLayout float leadingMainDim = 0; float betweenMainDim = 0; - // The remaining available space that needs to be allocated - float remainingMainDim = 0; - if (isMainDimDefined) { - remainingMainDim = definedMainDim - mainContentDim; - } else { - remainingMainDim = Math.Max(mainContentDim, 0) - mainContentDim; + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + float remainingFreeSpace = 0; + if (!float.IsNaN(availableInnerMainDim)) { + remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; + } else if (sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized based on its content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine; + } + + float remainingFreeSpaceAfterFlex = remainingFreeSpace; + + if (!canSkipFlex) { + float childFlexBasis; + float flexShrinkScaledFactor; + float flexGrowFactor; + float baseMainSize; + float boundMainSize; + + // Do two passes over the flex items to figure out how to distribute the remaining space. + // The first pass finds the items whose min/max constraints trigger, freezes them at those + // sizes, and excludes those sizes from the remaining space. The second pass sets the size + // of each flexible item. It distributes the remaining space amongst the items whose min/max + // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing + // their min/max constraints to trigger again. + // + // This two pass approach for resolving min/max constraints deviates from the spec. The + // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process + // that needs to be repeated a variable number of times. The algorithm implemented here + // won't handle all cases but it was simpler to implement and it mitigates performance + // concerns because we know exactly how many passes it'll do. + + // First pass: detect the flex items whose min/max constraints trigger + float deltaFreeSpace = 0; + float deltaFlexShrinkScaledFactors = 0; + float deltaFlexGrowFactors = 0; + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != null) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; + } + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexGrowFactors -= flexGrowFactor; + } + } + } + + currentRelativeChild = currentRelativeChild.nextChild; + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; + totalFlexGrowFactors += deltaFlexGrowFactors; + remainingFreeSpace += deltaFreeSpace; + remainingFreeSpaceAfterFlex = remainingFreeSpace; + + // Second pass: resolve the sizes of the flexible items + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != null) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + float updatedMainSize = childFlexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor); + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor); + } + } + + remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + + if (isMainAxisRow) { + childWidth = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.Exactly; + + if (!(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = float.IsNaN(childHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.AtMost; + } else { + childHeight = currentRelativeChild.style.dimensions[DIMENSION_HEIGHT] + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.Exactly; + } + } else { + childHeight = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.Exactly; + + if (!(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = float.IsNaN(childWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.AtMost; + } else { + childWidth = currentRelativeChild.style.dimensions[DIMENSION_WIDTH] + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.Exactly; + } + } + + boolean requiresStretchLayout = !(currentRelativeChild.style.dimensions[dim[crossAxis]] >= 0.0) && + getAlignItem(node, currentRelativeChild) == CSSAlign.Stretch; + + // Recursively call the layout algorithm for this child with the updated main size. + layoutNodeInternal(layoutContext, currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, "flex"); + + currentRelativeChild = currentRelativeChild.nextChild; + } + } + + remainingFreeSpace = remainingFreeSpaceAfterFlex; + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main axis. + // Their dimensions are also set in the cross axis with the exception of items + // that are aligned "stretch". We need to compute these stretch values and + // set the final positions. + + // If we are using "at most" rules in the main axis, we won't distribute + // any remaining space at this point. + if (measureModeMainDim == CSSMeasureMode.AtMost) { + remainingFreeSpace = 0; } - // If there are flexible children in the mix, they are going to fill the - // remaining space - if (flexibleChildrenCount != 0) { - float flexibleMainDim = remainingMainDim / totalFlexible; - float baseMainDim; - float boundMainDim; - - // If the flex share of remaining space doesn't meet min/max bounds, - // remove this child from flex calculations. - currentFlexChild = firstFlexChild; - while (currentFlexChild != null) { - baseMainDim = flexibleMainDim * currentFlexChild.style.flex + - ((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))); - boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); - - if (baseMainDim != boundMainDim) { - remainingMainDim -= boundMainDim; - totalFlexible -= currentFlexChild.style.flex; - } - - currentFlexChild = currentFlexChild.nextFlexChild; - } - flexibleMainDim = remainingMainDim / totalFlexible; - - // The non flexible children can overflow the container, in this case - // we should just assume that there is no space available. - if (flexibleMainDim < 0) { - flexibleMainDim = 0; - } - - currentFlexChild = firstFlexChild; - while (currentFlexChild != null) { - // At this point we know the final size of the element in the main - // dimension - currentFlexChild.layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, - flexibleMainDim * currentFlexChild.style.flex + - ((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) - ); - - maxWidth = CSSConstants.Undefined; - if ((!float.IsNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0)) { - maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) - - paddingAndBorderAxisResolvedRow; - } - maxHeight = CSSConstants.Undefined; - if ((!float.IsNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else if (isMainRowDirection) { - maxHeight = parentMaxHeight - - (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) - - paddingAndBorderAxisColumn; - } - - // And we recursively call the layout algorithm for this child - layoutNode(layoutContext, currentFlexChild, maxWidth, maxHeight, direction); - - child = currentFlexChild; - currentFlexChild = currentFlexChild.nextFlexChild; - child.nextFlexChild = null; - } - - // We use justifyContent to figure out how to allocate the remaining - // space available - } else if (justifyContent != CSSJustify.FlexStart) { + // Use justifyContent to figure out how to allocate the remaining space + // available in the main axis. + if (justifyContent != CSSJustify.FlexStart) { if (justifyContent == CSSJustify.Center) { - leadingMainDim = remainingMainDim / 2; + leadingMainDim = remainingFreeSpace / 2; } else if (justifyContent == CSSJustify.FlexEnd) { - leadingMainDim = remainingMainDim; + leadingMainDim = remainingFreeSpace; } else if (justifyContent == CSSJustify.SpaceBetween) { - remainingMainDim = Math.Max(remainingMainDim, 0); - if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) { - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + remainingFreeSpace = Math.Max(remainingFreeSpace, 0); + if (itemsOnLine > 1) { + betweenMainDim = remainingFreeSpace / (itemsOnLine - 1); } else { betweenMainDim = 0; } } else if (justifyContent == CSSJustify.SpaceAround) { // Space on the edges is half of the space between elements - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount); + betweenMainDim = remainingFreeSpace / itemsOnLine; leadingMainDim = betweenMainDim / 2; } } - // Position elements in the main axis and compute dimensions + float mainDim = leadingPaddingAndBorderMain + leadingMainDim; + float crossDim = 0; - // At this point, all the children have their dimensions set. We need to - // find their position. In order to do that, we accumulate data in - // variables that are also useful to compute the total dimensions of the - // container! - mainDim += leadingMainDim; - - for (i = firstComplexMain; i < endLine; ++i) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { child = node.getChildAt(i); if (child.style.positionType == CSSPositionType.Absolute && !float.IsNaN(child.style.position[leading[mainAxis]])) { - // In case the child is position absolute and has left/top being - // defined, we override the position to whatever the user said - // (and margin/border). - child.layout.position[pos[mainAxis]] = (float.IsNaN(child.style.position[leading[mainAxis]]) ? 0 : child.style.position[leading[mainAxis]]) + - node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + - child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]); - } else { - // If the child is position absolute (without top/left) or relative, - // we put it at the current accumulated offset. - child.layout.position[pos[mainAxis]] += mainDim; - - // Define the trailing position accordingly. - if (isMainDimDefined) { - child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]]; + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child.layout.position[pos[mainAxis]] = (float.IsNaN(child.style.position[leading[mainAxis]]) ? 0 : child.style.position[leading[mainAxis]]) + + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]); } - - // Now that we placed the element, we need to update the variables - // We only need to do that for relative elements. Absolute elements + } else { + if (performLayout) { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child.layout.position[pos[mainAxis]] += mainDim; + } + + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements // do not take part in that phase. if (child.style.positionType == CSSPositionType.Relative) { - // The main dimension is the sum of all the elements dimension plus - // the spacing. - mainDim += betweenMainDim + (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - // The cross dimension is the max of the elements dimension since there - // can only be one element in that cross dimension. - crossDim = Math.Max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))); + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims because + // they weren't computed. This means we can't call getDimWithMargin. + mainDim += betweenMainDim + (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])) + child.layout.flexBasis; + crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + (child.layout.measuredDimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); + + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = Math.Max(crossDim, (child.layout.measuredDimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); + } } } } - float containerCrossAxis = node.layout.dimensions[dim[crossAxis]]; - if (!isCrossDimDefined) { - containerCrossAxis = Math.Max( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); + mainDim += trailingPaddingAndBorderMain; + + float containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim == CSSMeasureMode.Undefined || measureModeCrossDim == CSSMeasureMode.AtMost) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; + + if (measureModeCrossDim == CSSMeasureMode.AtMost) { + containerCrossAxis = Math.Min(containerCrossAxis, availableInnerCrossDim); + } } - // Position elements in the cross axis - for (i = firstComplexCross; i < endLine; ++i) { - child = node.getChildAt(i); + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim == CSSMeasureMode.Exactly) { + crossDim = availableInnerCrossDim; + } - if (child.style.positionType == CSSPositionType.Absolute && - !float.IsNaN(child.style.position[leading[crossAxis]])) { - // In case the child is absolutely positionned and has a - // top/left/bottom/right being set, we override all the previously - // computed positions to set it correctly. - child.layout.position[pos[crossAxis]] = (float.IsNaN(child.style.position[leading[crossAxis]]) ? 0 : child.style.position[leading[crossAxis]]) + - node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + - child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + // Clamp to the min/max size specified on the container. + crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; - } else { - float leadingCrossDim = leadingPaddingAndBorderCross; + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { + child = node.getChildAt(i); - // For a relative children, we're either using alignItems (parent) or - // alignSelf (child) in order to determine the position in the cross axis - if (child.style.positionType == CSSPositionType.Relative) { - /*eslint-disable */ - // This variable is intentionally re-defined as the code is transpiled to a block scope language + if (child.style.positionType == CSSPositionType.Absolute) { + // If the child is absolutely positioned and has a top/left/bottom/right + // set, override all the previously computed positions to set it correctly. + if (!float.IsNaN(child.style.position[leading[crossAxis]])) { + child.layout.position[pos[crossAxis]] = (float.IsNaN(child.style.position[leading[crossAxis]]) ? 0 : child.style.position[leading[crossAxis]]) + + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + } else { + child.layout.position[pos[crossAxis]] = leadingPaddingAndBorderCross + + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis CSSAlign alignItem = getAlignItem(node, child); - /*eslint-enable */ + + // If the child uses align stretch, we need to lay it out one more time, this time + // forcing the cross-axis size to be the computed cross size for the current line. if (alignItem == CSSAlign.Stretch) { - // You can only stretch if the dimension has not already been defined - // previously. - if (!(!float.IsNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] >= 0.0)) { - float dimCrossAxis = child.layout.dimensions[dim[crossAxis]]; - child.layout.dimensions[dim[crossAxis]] = Math.Max( - boundAxis(child, crossAxis, containerCrossAxis - - paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))), - // You never want to go smaller than padding - ((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))) - ); - - // If the size has changed, and this child has children we need to re-layout this child - if (dimCrossAxis != child.layout.dimensions[dim[crossAxis]] && child.getChildCount() > 0) { - // Reset child margins before re-layout as they are added back in layoutNode and would be doubled - child.layout.position[leading[mainAxis]] -= child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + - getRelativePosition(child, mainAxis); - child.layout.position[trailing[mainAxis]] -= child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + - getRelativePosition(child, mainAxis); - child.layout.position[leading[crossAxis]] -= child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + - getRelativePosition(child, crossAxis); - child.layout.position[trailing[crossAxis]] -= child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + - getRelativePosition(child, crossAxis); - - layoutNode(layoutContext, child, maxWidth, maxHeight, direction); - } + childWidth = child.layout.measuredDimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childHeight = child.layout.measuredDimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + boolean isCrossSizeDefinite = false; + + if (isMainAxisRow) { + isCrossSizeDefinite = (child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0); + childHeight = crossDim; + } else { + isCrossSizeDefinite = (child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0); + childWidth = crossDim; + } + + // If the child defines a definite size for its cross axis, there's no need to stretch. + if (!isCrossSizeDefinite) { + childWidthMeasureMode = float.IsNaN(childWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly; + childHeightMeasureMode = float.IsNaN(childHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly; + layoutNodeInternal(layoutContext, child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, "stretch"); } } else if (alignItem != CSSAlign.FlexStart) { - // The remaining space between the parent dimensions+padding and child - // dimensions+margin. - float remainingCrossDim = containerCrossAxis - - paddingAndBorderAxisCross - (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])); + float remainingCrossDim = containerCrossAxis - (child.layout.measuredDimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])); if (alignItem == CSSAlign.Center) { leadingCrossDim += remainingCrossDim / 2; @@ -813,41 +1071,25 @@ namespace Facebook.CSSLayout leadingCrossDim += remainingCrossDim; } } - } - // And we apply the position - child.layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; - - // Define the trailing position accordingly. - if (isCrossDimDefined) { - child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]]; + // And we apply the position + child.layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; } } } - linesCrossDim += crossDim; - linesMainDim = Math.Max(linesMainDim, mainDim); - linesCount += 1; - startLine = endLine; + totalLineCrossDim += crossDim; + maxLineMainDim = Math.Max(maxLineMainDim, mainDim); + + // Reset variables for new line. + lineCount++; + startOfLineIndex = endOfLineIndex; + endOfLineIndex = startOfLineIndex; } - // - // - // Note(prenaux): More than one line, we need to layout the crossAxis - // according to alignContent. - // - // Note that we could probably remove and handle the one line case - // here too, but for the moment this is safer since it won't interfere with - // previously working code. - // - // See specs: - // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm - // section 9.4 - // - if (linesCount > 1 && isCrossDimDefined) { - float nodeCrossAxisInnerSize = node.layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross; - float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (lineCount > 1 && performLayout && !float.IsNaN(availableInnerCrossDim)) { + float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; float crossDimLead = 0; float currentLead = leadingPaddingAndBorderCross; @@ -858,53 +1100,54 @@ namespace Facebook.CSSLayout } else if (alignContent == CSSAlign.Center) { currentLead += remainingAlignContentDim / 2; } else if (alignContent == CSSAlign.Stretch) { - if (nodeCrossAxisInnerSize > linesCrossDim) { - crossDimLead = (remainingAlignContentDim / linesCount); + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = (remainingAlignContentDim / lineCount); } } int endIndex = 0; - for (i = 0; i < linesCount; ++i) { + for (i = 0; i < lineCount; ++i) { int startIndex = endIndex; + int j; // compute the line's height and find the endIndex float lineHeight = 0; - for (ii = startIndex; ii < childCount; ++ii) { - child = node.getChildAt(ii); + for (j = startIndex; j < childCount; ++j) { + child = node.getChildAt(j); if (child.style.positionType != CSSPositionType.Relative) { continue; } if (child.lineIndex != i) { break; } - if ((!float.IsNaN(child.layout.dimensions[dim[crossAxis]]) && child.layout.dimensions[dim[crossAxis]] >= 0.0)) { - lineHeight = Math.Max( - lineHeight, - child.layout.dimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])) - ); + if ((child.layout.measuredDimensions[dim[crossAxis]] >= 0.0)) { + lineHeight = Math.Max(lineHeight, + child.layout.measuredDimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); } } - endIndex = ii; + endIndex = j; lineHeight += crossDimLead; - for (ii = startIndex; ii < endIndex; ++ii) { - child = node.getChildAt(ii); - if (child.style.positionType != CSSPositionType.Relative) { - continue; - } + if (performLayout) { + for (j = startIndex; j < endIndex; ++j) { + child = node.getChildAt(j); + if (child.style.positionType != CSSPositionType.Relative) { + continue; + } - CSSAlign alignContentAlignItem = getAlignItem(node, child); - if (alignContentAlignItem == CSSAlign.FlexStart) { - child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); - } else if (alignContentAlignItem == CSSAlign.FlexEnd) { - child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.dimensions[dim[crossAxis]]; - } else if (alignContentAlignItem == CSSAlign.Center) { - float childHeight = child.layout.dimensions[dim[crossAxis]]; - child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; - } else if (alignContentAlignItem == CSSAlign.Stretch) { - child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); - // TODO(prenaux): Correctly set the height of items with undefined - // (auto) crossAxis dimension. + CSSAlign alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSSAlign.FlexStart) { + child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + } else if (alignContentAlignItem == CSSAlign.FlexEnd) { + child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.measuredDimensions[dim[crossAxis]]; + } else if (alignContentAlignItem == CSSAlign.Center) { + childHeight = child.layout.measuredDimensions[dim[crossAxis]]; + child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem == CSSAlign.Stretch) { + child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + // TODO(prenaux): Correctly set the height of items with indefinite + // (auto) crossAxis dimension. + } } } @@ -912,94 +1155,149 @@ namespace Facebook.CSSLayout } } - boolean needsMainTrailingPos = false; - boolean needsCrossTrailingPos = false; + // STEP 9: COMPUTING FINAL DIMENSIONS + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (!isMainDimDefined) { - node.layout.dimensions[dim[mainAxis]] = Math.Max( - // We're missing the last padding at this point to get the final - // dimension - boundAxis(node, mainAxis, linesMainDim + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))), - // We can never assign a width smaller than the padding and borders - paddingAndBorderAxisMain - ); + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim == CSSMeasureMode.Undefined) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout.measuredDimensions[dim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim); + } else if (measureModeMainDim == CSSMeasureMode.AtMost) { + node.layout.measuredDimensions[dim[mainAxis]] = Math.Max( + Math.Min(availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)), + paddingAndBorderAxisMain); + } + + if (measureModeCrossDim == CSSMeasureMode.Undefined) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout.measuredDimensions[dim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross); + } else if (measureModeCrossDim == CSSMeasureMode.AtMost) { + node.layout.measuredDimensions[dim[crossAxis]] = Math.Max( + Math.Min(availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)), + paddingAndBorderAxisCross); + } + + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + if (performLayout) { + boolean needsMainTrailingPos = false; + boolean needsCrossTrailingPos = false; if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsMainTrailingPos = true; } - } - - if (!isCrossDimDefined) { - node.layout.dimensions[dim[crossAxis]] = Math.Max( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsCrossTrailingPos = true; } - } - // Set trailing position if necessary - if (needsMainTrailingPos || needsCrossTrailingPos) { - for (i = 0; i < childCount; ++i) { - child = node.getChildAt(i); + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node.getChildAt(i); - if (needsMainTrailingPos) { - child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]]; - } + if (needsMainTrailingPos) { + child.layout.position[trailing[mainAxis]] = node.layout.measuredDimensions[dim[mainAxis]] - (child.style.positionType == CSSPositionType.Absolute ? 0 : child.layout.measuredDimensions[dim[mainAxis]]) - child.layout.position[pos[mainAxis]]; + } - if (needsCrossTrailingPos) { - child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]]; + if (needsCrossTrailingPos) { + child.layout.position[trailing[crossAxis]] = node.layout.measuredDimensions[dim[crossAxis]] - (child.style.positionType == CSSPositionType.Absolute ? 0 : child.layout.measuredDimensions[dim[crossAxis]]) - child.layout.position[pos[crossAxis]]; + } } } } - - // Calculate dimensions for absolutely positioned elements + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN currentAbsoluteChild = firstAbsoluteChild; while (currentAbsoluteChild != null) { - // Pre-fill dimensions when using absolute position and both offsets for - // the axis are defined (either both left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + // Now that we know the bounds of the container, perform layout again on the + // absolutely-positioned children. + if (performLayout) { - if ((!float.IsNaN(node.layout.dimensions[dim[axis]]) && node.layout.dimensions[dim[axis]] >= 0.0) && - !(!float.IsNaN(currentAbsoluteChild.style.dimensions[dim[axis]]) && currentAbsoluteChild.style.dimensions[dim[axis]] >= 0.0) && - !float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) && - !float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]])) { - currentAbsoluteChild.layout.dimensions[dim[axis]] = Math.Max( - boundAxis(currentAbsoluteChild, axis, node.layout.dimensions[dim[axis]] - - (node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])) - - (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) - - (float.IsNaN(currentAbsoluteChild.style.position[leading[axis]]) ? 0 : currentAbsoluteChild.style.position[leading[axis]]) - - (float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]]) - ), - // You never want to go smaller than padding - ((currentAbsoluteChild.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (currentAbsoluteChild.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + currentAbsoluteChild.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) - ); + childWidth = CSSConstants.Undefined; + childHeight = CSSConstants.Undefined; + + if ((currentAbsoluteChild.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = currentAbsoluteChild.style.dimensions[DIMENSION_WIDTH] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + } else { + // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined. + if (!float.IsNaN(currentAbsoluteChild.style.position[POSITION_LEFT]) && !float.IsNaN(currentAbsoluteChild.style.position[POSITION_RIGHT])) { + childWidth = node.layout.measuredDimensions[DIMENSION_WIDTH] - + (node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])) - + (currentAbsoluteChild.style.position[POSITION_LEFT] + currentAbsoluteChild.style.position[POSITION_RIGHT]); + childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth); + } + } + + if ((currentAbsoluteChild.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = currentAbsoluteChild.style.dimensions[DIMENSION_HEIGHT] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + } else { + // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined. + if (!float.IsNaN(currentAbsoluteChild.style.position[POSITION_TOP]) && !float.IsNaN(currentAbsoluteChild.style.position[POSITION_BOTTOM])) { + childHeight = node.layout.measuredDimensions[DIMENSION_HEIGHT] - + (node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) - + (currentAbsoluteChild.style.position[POSITION_TOP] + currentAbsoluteChild.style.position[POSITION_BOTTOM]); + childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight); + } } - if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) && - !!float.IsNaN(currentAbsoluteChild.style.position[leading[axis]])) { - currentAbsoluteChild.layout.position[leading[axis]] = - node.layout.dimensions[dim[axis]] - - currentAbsoluteChild.layout.dimensions[dim[axis]] - - (float.IsNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]]); + // If we're still missing one or the other dimension, measure the content. + if (float.IsNaN(childWidth) || float.IsNaN(childHeight)) { + childWidthMeasureMode = float.IsNaN(childWidth) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly; + childHeightMeasureMode = float.IsNaN(childHeight) ? CSSMeasureMode.Undefined : CSSMeasureMode.Exactly; + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && float.IsNaN(childWidth) && !float.IsNaN(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSSMeasureMode.AtMost; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node.style.overflow == CSSOverflow.Hidden) { + if (isMainAxisRow && float.IsNaN(childHeight) && !float.IsNaN(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSSMeasureMode.AtMost; + } + } + + layoutNodeInternal(layoutContext, currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "abs-measure"); + childWidth = currentAbsoluteChild.layout.measuredDimensions[DIMENSION_WIDTH] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childHeight = currentAbsoluteChild.layout.measuredDimensions[DIMENSION_HEIGHT] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + } + + layoutNodeInternal(layoutContext, currentAbsoluteChild, childWidth, childHeight, direction, CSSMeasureMode.Exactly, CSSMeasureMode.Exactly, true, "abs-layout"); + + if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]) && + !!float.IsNaN(currentAbsoluteChild.style.position[leading[CSS_FLEX_DIRECTION_ROW]])) { + currentAbsoluteChild.layout.position[leading[CSS_FLEX_DIRECTION_ROW]] = + node.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + currentAbsoluteChild.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + (float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]) ? 0 : currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]); + } + + if (!float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]) && + !!float.IsNaN(currentAbsoluteChild.style.position[leading[CSS_FLEX_DIRECTION_COLUMN]])) { + currentAbsoluteChild.layout.position[leading[CSS_FLEX_DIRECTION_COLUMN]] = + node.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + currentAbsoluteChild.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + (float.IsNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]) ? 0 : currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]); } } - child = currentAbsoluteChild; - currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild; - child.nextAbsoluteChild = null; + currentAbsoluteChild = currentAbsoluteChild.nextChild; } - } /** END_GENERATED **/ + } } } diff --git a/src/java/src/com/facebook/csslayout/CSSCachedMeasurement.java b/src/java/src/com/facebook/csslayout/CSSCachedMeasurement.java new file mode 100644 index 00000000..92b3f77b --- /dev/null +++ b/src/java/src/com/facebook/csslayout/CSSCachedMeasurement.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +package com.facebook.csslayout; + +public class CSSCachedMeasurement { + public float availableWidth; + public float availableHeight; + public CSSMeasureMode widthMeasureMode = null; + public CSSMeasureMode heightMeasureMode = null; + + public float computedWidth; + public float computedHeight; +} diff --git a/src/java/src/com/facebook/csslayout/CSSLayout.java b/src/java/src/com/facebook/csslayout/CSSLayout.java index 26ec4d56..a484bd94 100644 --- a/src/java/src/com/facebook/csslayout/CSSLayout.java +++ b/src/java/src/com/facebook/csslayout/CSSLayout.java @@ -14,6 +14,10 @@ import java.util.Arrays; * Where the output of {@link LayoutEngine#layoutNode(CSSNode, float)} will go in the CSSNode. */ public class CSSLayout { + // This value was chosen based on empiracle data. Even the most complicated + // layouts should not require more than 16 entries to fit within the cache. + public static final int MAX_CACHED_RESULT_COUNT = 16; + public static final int POSITION_LEFT = 0; public static final int POSITION_TOP = 1; public static final int POSITION_RIGHT = 2; @@ -25,24 +29,38 @@ public class CSSLayout { public float[] position = new float[4]; public float[] dimensions = new float[2]; public CSSDirection direction = CSSDirection.LTR; - - /** - * This should always get called before calling {@link LayoutEngine#layoutNode(CSSNode, float)} - */ + + public float flexBasis; + + public int generationCount; + public CSSDirection lastParentDirection; + + public int nextCachedMeasurementsIndex; + public CSSCachedMeasurement[] cachedMeasurements = new CSSCachedMeasurement[MAX_CACHED_RESULT_COUNT]; + public float[] measuredDimensions = new float[2]; + + public CSSCachedMeasurement cachedLayout = new CSSCachedMeasurement(); + + CSSLayout() { + resetResult(); + } + public void resetResult() { Arrays.fill(position, 0); Arrays.fill(dimensions, CSSConstants.UNDEFINED); direction = CSSDirection.LTR; - } - - public void copy(CSSLayout layout) { - position[POSITION_LEFT] = layout.position[POSITION_LEFT]; - position[POSITION_TOP] = layout.position[POSITION_TOP]; - position[POSITION_RIGHT] = layout.position[POSITION_RIGHT]; - position[POSITION_BOTTOM] = layout.position[POSITION_BOTTOM]; - dimensions[DIMENSION_WIDTH] = layout.dimensions[DIMENSION_WIDTH]; - dimensions[DIMENSION_HEIGHT] = layout.dimensions[DIMENSION_HEIGHT]; - direction = layout.direction; + + flexBasis = 0; + + generationCount = 0; + lastParentDirection = null; + + nextCachedMeasurementsIndex = 0; + measuredDimensions[DIMENSION_WIDTH] = CSSConstants.UNDEFINED; + measuredDimensions[DIMENSION_HEIGHT] = CSSConstants.UNDEFINED; + + cachedLayout.widthMeasureMode = null; + cachedLayout.heightMeasureMode = null; } @Override diff --git a/src/java/src/com/facebook/csslayout/CSSLayoutContext.java b/src/java/src/com/facebook/csslayout/CSSLayoutContext.java index 8c93e0eb..64612e3a 100644 --- a/src/java/src/com/facebook/csslayout/CSSLayoutContext.java +++ b/src/java/src/com/facebook/csslayout/CSSLayoutContext.java @@ -17,4 +17,5 @@ package com.facebook.csslayout; */ public class CSSLayoutContext { /*package*/ final MeasureOutput measureOutput = new MeasureOutput(); + int currentGenerationCount; } diff --git a/src/java/src/com/facebook/csslayout/CSSNode.java b/src/java/src/com/facebook/csslayout/CSSNode.java index 87c63124..011d98ed 100644 --- a/src/java/src/com/facebook/csslayout/CSSNode.java +++ b/src/java/src/com/facebook/csslayout/CSSNode.java @@ -63,9 +63,8 @@ public class CSSNode { public int lineIndex = 0; - /*package*/ CSSNode nextAbsoluteChild; - /*package*/ CSSNode nextFlexChild; - + /*package*/ CSSNode nextChild; + private @Nullable ArrayList mChildren; private @Nullable CSSNode mParent; private @Nullable MeasureFunction mMeasureFunction = null; @@ -139,7 +138,6 @@ public class CSSNode { * Performs the actual layout and saves the results in {@link #layout} */ public void calculateLayout(CSSLayoutContext layoutContext) { - layout.resetResult(); LayoutEngine.layoutNode(layoutContext, this, CSSConstants.UNDEFINED, CSSConstants.UNDEFINED, null); } diff --git a/src/java/src/com/facebook/csslayout/CSSOverflow.java b/src/java/src/com/facebook/csslayout/CSSOverflow.java new file mode 100644 index 00000000..d8201a37 --- /dev/null +++ b/src/java/src/com/facebook/csslayout/CSSOverflow.java @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ +package com.facebook.csslayout; + +public enum CSSOverflow { + VISIBLE, + HIDDEN, +} diff --git a/src/java/src/com/facebook/csslayout/CSSStyle.java b/src/java/src/com/facebook/csslayout/CSSStyle.java index f4d325dc..ccb79265 100644 --- a/src/java/src/com/facebook/csslayout/CSSStyle.java +++ b/src/java/src/com/facebook/csslayout/CSSStyle.java @@ -23,6 +23,7 @@ public class CSSStyle { public CSSAlign alignSelf; public CSSPositionType positionType; public CSSWrap flexWrap; + public CSSOverflow overflow; public float flex; public Spacing margin = new Spacing(); @@ -51,6 +52,7 @@ public class CSSStyle { alignSelf = CSSAlign.AUTO; positionType = CSSPositionType.RELATIVE; flexWrap = CSSWrap.NOWRAP; + overflow = CSSOverflow.VISIBLE; flex = 0f; margin.reset();; diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index 99fe9100..92965141 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -8,6 +8,8 @@ */ package com.facebook.csslayout; +import com.facebook.infer.annotation.Assertions; + import static com.facebook.csslayout.CSSLayout.DIMENSION_HEIGHT; import static com.facebook.csslayout.CSSLayout.DIMENSION_WIDTH; import static com.facebook.csslayout.CSSLayout.POSITION_BOTTOM; @@ -19,7 +21,9 @@ import static com.facebook.csslayout.CSSLayout.POSITION_TOP; * Calculates layouts based on CSS style. See {@link #layoutNode(CSSNode, float, float)}. */ public class LayoutEngine { - + + private static final boolean POSITIVE_FLEX_IS_AUTO = false; + private static final int CSS_FLEX_DIRECTION_COLUMN = CSSFlexDirection.COLUMN.ordinal(); private static final int CSS_FLEX_DIRECTION_COLUMN_REVERSE = @@ -73,8 +77,42 @@ public class LayoutEngine { Spacing.END, Spacing.END }; + + private static boolean isFlexBasisAuto(CSSNode node) { + if (POSITIVE_FLEX_IS_AUTO) { + // All flex values are auto. + return true; + } else { + // A flex value > 0 implies a basis of zero. + return node.style.flex <= 0; + } + } + + private static float getFlexGrowFactor(CSSNode node) { + // Flex grow is implied by positive values for flex. + if (node.style.flex > 0) { + return node.style.flex; + } + return 0; + } + + private static float getFlexShrinkFactor(CSSNode node) { + if (POSITIVE_FLEX_IS_AUTO) { + // A flex shrink factor of 1 is implied by non-zero values for flex. + if (node.style.flex != 0) { + return 1; + } + } else { + // A flex shrink factor of 1 is implied by negative values for flex. + if (node.style.flex < 0) { + return 1; + } + } + return 0; + } - private static float boundAxis(CSSNode node, int axis, float value) { + + private static float boundAxisWithinMinAndMax(CSSNode node, int axis, float value) { float min = CSSConstants.UNDEFINED; float max = CSSConstants.UNDEFINED; @@ -99,26 +137,14 @@ public class LayoutEngine { return boundValue; } - - private static void setDimensionFromStyle(CSSNode node, int axis) { - // The parent already computed us a width or height. We just skip it - if (!Float.isNaN(node.layout.dimensions[dim[axis]])) { - return; - } - // We only run if there's a width or height defined - if (Float.isNaN(node.style.dimensions[dim[axis]]) || - node.style.dimensions[dim[axis]] <= 0.0) { - return; - } - - // The dimensions can never be smaller than the padding and border - float maxLayoutDimension = Math.max( - boundAxis(node, axis, node.style.dimensions[dim[axis]]), - node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + - node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + - node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + - node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])); - node.layout.dimensions[dim[axis]] = maxLayoutDimension; + + private static float boundAxis(CSSNode node, int axis, float value) { + float paddingAndBorderAxis = + node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + + node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + + node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]); + return Math.max(boundAxisWithinMinAndMax(node, axis, value), paddingAndBorderAxis); } private static float getRelativePosition(CSSNode node, int axis) { @@ -130,6 +156,20 @@ public class LayoutEngine { float trailingPos = node.style.position[trailing[axis]]; return Float.isNaN(trailingPos) ? 0 : -trailingPos; } + + private static void setPosition(CSSNode node, CSSDirection direction) { + int mainAxis = resolveAxis(getFlexDirection(node), direction); + int crossAxis = getCrossFlexDirection(mainAxis, direction); + + node.layout.position[leading[mainAxis]] = node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + + getRelativePosition(node, mainAxis); + node.layout.position[trailing[mainAxis]] = node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + + getRelativePosition(node, mainAxis); + node.layout.position[leading[crossAxis]] = node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + + getRelativePosition(node, crossAxis); + node.layout.position[trailing[crossAxis]] = node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + + getRelativePosition(node, crossAxis); + } private static int resolveAxis( int axis, @@ -180,395 +220,516 @@ public class LayoutEngine { return node.isMeasureDefined(); } - static boolean needsRelayout(CSSNode node, float parentMaxWidth, float parentMaxHeight) { - return node.isDirty() || - !FloatUtil.floatsEqual( - node.lastLayout.requestedHeight, - node.layout.dimensions[DIMENSION_HEIGHT]) || - !FloatUtil.floatsEqual( - node.lastLayout.requestedWidth, - node.layout.dimensions[DIMENSION_WIDTH]) || - !FloatUtil.floatsEqual(node.lastLayout.parentMaxWidth, parentMaxWidth) || - !FloatUtil.floatsEqual(node.lastLayout.parentMaxHeight, parentMaxHeight); - } - /*package*/ static void layoutNode( CSSLayoutContext layoutContext, CSSNode node, - float parentMaxWidth, - float parentMaxHeight, + float availableWidth, + float availableHeight, CSSDirection parentDirection) { - if (needsRelayout(node, parentMaxWidth, parentMaxHeight)) { - node.lastLayout.requestedWidth = node.layout.dimensions[DIMENSION_WIDTH]; - node.lastLayout.requestedHeight = node.layout.dimensions[DIMENSION_HEIGHT]; - node.lastLayout.parentMaxWidth = parentMaxWidth; - node.lastLayout.parentMaxHeight = parentMaxHeight; - - for (int i = 0, childCount = node.getChildCount(); i < childCount; i++) { - node.getChildAt(i).layout.resetResult(); - } - - layoutNodeImpl(layoutContext, node, parentMaxWidth, parentMaxHeight, parentDirection); - node.lastLayout.copy(node.layout); - } else { - node.layout.copy(node.lastLayout); + // Increment the generation count. This will force the recursive routine to visit + // all dirty nodes at least once. Subsequent visits will be skipped if the input + // parameters don't change. + layoutContext.currentGenerationCount++; + + // If the caller didn't specify a height/width, use the dimensions + // specified in the style. + if (Float.isNaN(availableWidth) && node.style.dimensions[DIMENSION_WIDTH] >= 0.0) { + float marginAxisRow = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + availableWidth = node.style.dimensions[DIMENSION_WIDTH] + marginAxisRow; + } + if (Float.isNaN(availableHeight) && node.style.dimensions[DIMENSION_HEIGHT] >= 0.0) { + float marginAxisColumn = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + availableHeight = node.style.dimensions[DIMENSION_HEIGHT] + marginAxisColumn; + } + + CSSMeasureMode widthMeasureMode = Float.isNaN(availableWidth) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; + CSSMeasureMode heightMeasureMode = Float.isNaN(availableHeight) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; + + if (layoutNodeInternal(layoutContext, node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, "initial")) { + setPosition(node, node.layout.direction); } - - node.markHasNewLayout(); } + + // + // This is a wrapper around the layoutNodeImpl function. It determines + // whether the layout request is redundant and can be skipped. + // + // Parameters: + // Input parameters are the same as layoutNodeImpl (see below) + // Return parameter is true if layout was performed, false if skipped + // + private static boolean layoutNodeInternal( + CSSLayoutContext layoutContext, + CSSNode node, + float availableWidth, + float availableHeight, + CSSDirection parentDirection, + CSSMeasureMode widthMeasureMode, + CSSMeasureMode heightMeasureMode, + boolean performLayout, + String reason) { + CSSLayout layout = node.layout; + + boolean needToVisitNode = (node.isDirty() && layout.generationCount != layoutContext.currentGenerationCount) || + layout.lastParentDirection != parentDirection; + + if (needToVisitNode) { + // Invalidate the cached results. + layout.nextCachedMeasurementsIndex = 0; + layout.cachedLayout.widthMeasureMode = null; + layout.cachedLayout.heightMeasureMode = null; + } + + CSSCachedMeasurement cachedResults = null; + + // Determine whether the results are already cached. We maintain a separate + // cache for layouts and measurements. A layout operation modifies the positions + // and dimensions for nodes in the subtree. The algorithm assumes that each node + // gets layed out a maximum of one time per tree layout, but multiple measurements + // may be required to resolve all of the flex dimensions. + if (performLayout) { + if (FloatUtil.floatsEqual(layout.cachedLayout.availableWidth, availableWidth) && + FloatUtil.floatsEqual(layout.cachedLayout.availableHeight, availableHeight) && + layout.cachedLayout.widthMeasureMode == widthMeasureMode && + layout.cachedLayout.heightMeasureMode == heightMeasureMode) { + + cachedResults = layout.cachedLayout; + } + } else { + for (int i = 0; i < layout.nextCachedMeasurementsIndex; i++) { + if (FloatUtil.floatsEqual(layout.cachedMeasurements[i].availableWidth, availableWidth) && + FloatUtil.floatsEqual(layout.cachedMeasurements[i].availableHeight, availableHeight) && + layout.cachedMeasurements[i].widthMeasureMode == widthMeasureMode && + layout.cachedMeasurements[i].heightMeasureMode == heightMeasureMode) { + cachedResults = layout.cachedMeasurements[i]; + break; + } + } + } + + if (!needToVisitNode && cachedResults != null) { + layout.measuredDimensions[DIMENSION_WIDTH] = cachedResults.computedWidth; + layout.measuredDimensions[DIMENSION_HEIGHT] = cachedResults.computedHeight; + } else { + layoutNodeImpl(layoutContext, node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout); + + layout.lastParentDirection = parentDirection; + + if (cachedResults == null) { + if (layout.nextCachedMeasurementsIndex == CSSLayout.MAX_CACHED_RESULT_COUNT) { + layout.nextCachedMeasurementsIndex = 0; + } + + CSSCachedMeasurement newCacheEntry = null; + if (performLayout) { + // Use the single layout cache entry. + newCacheEntry = layout.cachedLayout; + } else { + // Allocate a new measurement cache entry. + newCacheEntry = layout.cachedMeasurements[layout.nextCachedMeasurementsIndex]; + if (newCacheEntry == null) { + newCacheEntry = new CSSCachedMeasurement(); + layout.cachedMeasurements[layout.nextCachedMeasurementsIndex] = newCacheEntry; + } + layout.nextCachedMeasurementsIndex++; + } + + newCacheEntry.availableWidth = availableWidth; + newCacheEntry.availableHeight = availableHeight; + newCacheEntry.widthMeasureMode = widthMeasureMode; + newCacheEntry.heightMeasureMode = heightMeasureMode; + newCacheEntry.computedWidth = layout.measuredDimensions[DIMENSION_WIDTH]; + newCacheEntry.computedHeight = layout.measuredDimensions[DIMENSION_HEIGHT]; + } + } + + if (performLayout) { + node.layout.dimensions[DIMENSION_WIDTH] = node.layout.measuredDimensions[DIMENSION_WIDTH]; + node.layout.dimensions[DIMENSION_HEIGHT] = node.layout.measuredDimensions[DIMENSION_HEIGHT]; + node.markHasNewLayout(); + } + + layout.generationCount = layoutContext.currentGenerationCount; + return (needToVisitNode || cachedResults == null); + } + + + // + // This is the main routine that implements a subset of the flexbox layout algorithm + // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/. + // + // Limitations of this algorithm, compared to the full standard: + // * Display property is always assumed to be 'flex' except for Text nodes, which + // are assumed to be 'inline-flex'. + // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are + // stacked in document order. + // * The 'order' property is not supported. The order of flex items is always defined + // by document order. + // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse' + // and 'hidden' are not supported. + // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The + // rarely-used 'wrap-reverse' is not supported. + // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and + // flexBasis, this algorithm supports only the three most common combinations: + // flex: 0 is equiavlent to flex: 0 0 auto + // flex: n (where n is a positive value) is equivalent to flex: n 1 auto + // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0 + // This is faster because the content doesn't need to be measured, but it's + // less flexible because the basis is always 0 and can't be overriden with + // the width/height attributes. + // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto + // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel + // values, and the default value is 0. + // * The 'baseline' value is not supported for alignItems and alignSelf properties. + // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be + // specified as pixel values, not as percentages. + // * There is no support for calculation of dimensions based on intrinsic aspect ratios + // (e.g. images). + // * There is no support for forced breaks. + // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text). + // + // Deviations from standard: + // * Section 4.5 of the spec indicates that all flex items have a default minimum + // main size. For text blocks, for example, this is the width of the widest word. + // Calculating the minimum width is expensive, so we forego it and assume a default + // minimum main size of 0. + // * Min/Max sizes in the main axis are not honored when resolving flexible lengths. + // * The spec indicates that the default value for 'flexDirection' is 'row', but + // the algorithm below assumes a default of 'column'. + // + // Input parameters: + // - node: current node to be sized and layed out + // - availableWidth & availableHeight: available size to be used for sizing the node + // or CSS_UNDEFINED if the size is not available; interpretation depends on layout + // flags + // - parentDirection: the inline (text) direction within the parent (left-to-right or + // right-to-left) + // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation) + // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation) + // - performLayout: specifies whether the caller is interested in just the dimensions + // of the node or it requires the entire node and its subtree to be layed out + // (with final positions) + // + // Details: + // This routine is called recursively to lay out subtrees of flexbox elements. It uses the + // information in node.style, which is treated as a read-only input. It is responsible for + // setting the layout.direction and layout.measured_dimensions fields for the input node as well + // as the layout.position and layout.line_index fields for its child nodes. The + // layout.measured_dimensions field includes any border or padding for the node but does + // not include margins. + // + // The spec describes four different layout modes: "fill available", "max content", "min content", + // and "fit content". Of these, we don't use "min content" because we don't support default + // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode + // from the spec (https://www.w3.org/TR/css3-sizing/#terms): + // - CSS_MEASURE_MODE_UNDEFINED: max content + // - CSS_MEASURE_MODE_EXACTLY: fill available + // - CSS_MEASURE_MODE_AT_MOST: fit content + // + // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of + // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension. + // private static void layoutNodeImpl( CSSLayoutContext layoutContext, CSSNode node, - float parentMaxWidth, - float parentMaxHeight, - CSSDirection parentDirection) { + float availableWidth, + float availableHeight, + CSSDirection parentDirection, + CSSMeasureMode widthMeasureMode, + CSSMeasureMode heightMeasureMode, + boolean performLayout) { /** START_GENERATED **/ + Assertions.assertCondition(Float.isNaN(availableWidth) ? widthMeasureMode == CSSMeasureMode.UNDEFINED : true, "availableWidth is indefinite so widthMeasureMode must be CSSMeasureMode.UNDEFINED"); + Assertions.assertCondition(Float.isNaN(availableHeight) ? heightMeasureMode == CSSMeasureMode.UNDEFINED : true, "availableHeight is indefinite so heightMeasureMode must be CSSMeasureMode.UNDEFINED"); + + float paddingAndBorderAxisRow = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]))); + float paddingAndBorderAxisColumn = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]))); + float marginAxisRow = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + float marginAxisColumn = (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + + // Set the resolved resolution in the node's layout. CSSDirection direction = resolveDirection(node, parentDirection); - int mainAxis = resolveAxis(getFlexDirection(node), direction); - int crossAxis = getCrossFlexDirection(mainAxis, direction); - int resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction); - - // Handle width and height style attributes - setDimensionFromStyle(node, mainAxis); - setDimensionFromStyle(node, crossAxis); - - // Set the resolved resolution in the node's layout node.layout.direction = direction; - // The position is set by the parent, but we need to complete it with a - // delta composed of the margin and left/top/right/bottom - node.layout.position[leading[mainAxis]] += node.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + - getRelativePosition(node, mainAxis); - node.layout.position[trailing[mainAxis]] += node.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + - getRelativePosition(node, mainAxis); - node.layout.position[leading[crossAxis]] += node.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + - getRelativePosition(node, crossAxis); - node.layout.position[trailing[crossAxis]] += node.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + - getRelativePosition(node, crossAxis); - - // Inline immutable values from the target node to avoid excessive method - // invocations during the layout calculation. - int childCount = node.getChildCount(); - float paddingAndBorderAxisResolvedRow = ((node.style.padding.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.border.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis])) + (node.style.padding.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]) + node.style.border.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis]))); - float paddingAndBorderAxisColumn = ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]))); - + // For content (text) nodes, determine the dimensions based on the text contents. if (isMeasureDefined(node)) { - boolean isResolvedRowDimDefined = (!Float.isNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0); + float innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + + if (widthMeasureMode == CSSMeasureMode.EXACTLY && heightMeasureMode == CSSMeasureMode.EXACTLY) { - float width = CSSConstants.UNDEFINED; - CSSMeasureMode widthMode = CSSMeasureMode.UNDEFINED; - if ((!Float.isNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0)) { - width = node.style.dimensions[DIMENSION_WIDTH]; - widthMode = CSSMeasureMode.EXACTLY; - } else if (isResolvedRowDimDefined) { - width = node.layout.dimensions[dim[resolvedRowAxis]]; - widthMode = CSSMeasureMode.EXACTLY; + // Don't bother sizing the text if both dimensions are already defined. + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); + } else if (innerWidth <= 0) { + + // Don't bother sizing the text if there's no horizontal space. + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { - width = parentMaxWidth - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])); - widthMode = CSSMeasureMode.AT_MOST; - } - width -= paddingAndBorderAxisResolvedRow; - if (Float.isNaN(width)) { - widthMode = CSSMeasureMode.UNDEFINED; - } - float height = CSSConstants.UNDEFINED; - CSSMeasureMode heightMode = CSSMeasureMode.UNDEFINED; - if ((!Float.isNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - height = node.style.dimensions[DIMENSION_HEIGHT]; - heightMode = CSSMeasureMode.EXACTLY; - } else if ((!Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - height = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]; - heightMode = CSSMeasureMode.EXACTLY; - } else { - height = parentMaxHeight - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])); - heightMode = CSSMeasureMode.AT_MOST; - } - height -= ((node.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (node.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]))); - if (Float.isNaN(height)) { - heightMode = CSSMeasureMode.UNDEFINED; - } - - // We only need to give a dimension for the text if we haven't got any - // for it computed yet. It can either be from the style attribute or because - // the element is flexible. - boolean isRowUndefined = !(!Float.isNaN(node.style.dimensions[dim[resolvedRowAxis]]) && node.style.dimensions[dim[resolvedRowAxis]] >= 0.0) && !isResolvedRowDimDefined; - boolean isColumnUndefined = !(!Float.isNaN(node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0) && - Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]); - - // Let's not measure the text if we already know both dimensions - if (isRowUndefined || isColumnUndefined) { + // Measure the text under the current constraints. MeasureOutput measureDim = node.measure( layoutContext.measureOutput, - width, - widthMode, - height, - heightMode + innerWidth, + widthMeasureMode, + innerHeight, + heightMeasureMode ); - if (isRowUndefined) { - node.layout.dimensions[DIMENSION_WIDTH] = measureDim.width + - paddingAndBorderAxisResolvedRow; - } - if (isColumnUndefined) { - node.layout.dimensions[DIMENSION_HEIGHT] = measureDim.height + - paddingAndBorderAxisColumn; - } + + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSSMeasureMode.UNDEFINED || widthMeasureMode == CSSMeasureMode.AT_MOST) ? + measureDim.width + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSSMeasureMode.UNDEFINED || heightMeasureMode == CSSMeasureMode.AT_MOST) ? + measureDim.height + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); } - if (childCount == 0) { + + return; + } + + // For nodes with no children, use the available values if they were provided, or + // the minimum size as indicated by the padding and border sizes. + int childCount = node.getChildCount(); + if (childCount == 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, + (widthMeasureMode == CSSMeasureMode.UNDEFINED || widthMeasureMode == CSSMeasureMode.AT_MOST) ? + paddingAndBorderAxisRow : + availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, + (heightMeasureMode == CSSMeasureMode.UNDEFINED || heightMeasureMode == CSSMeasureMode.AT_MOST) ? + paddingAndBorderAxisColumn : + availableHeight - marginAxisColumn); + return; + } + + // If we're not being asked to perform a full layout, we can handle a number of common + // cases here without incurring the cost of the remaining function. + if (!performLayout) { + // If we're being asked to size the content with an at most constraint but there is no available width, + // the measurement will always be zero. + if (widthMeasureMode == CSSMeasureMode.AT_MOST && availableWidth <= 0 && + heightMeasureMode == CSSMeasureMode.AT_MOST && availableHeight <= 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + if (widthMeasureMode == CSSMeasureMode.AT_MOST && availableWidth <= 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, Float.isNaN(availableHeight) ? 0 : (availableHeight - marginAxisColumn)); + return; + } + + if (heightMeasureMode == CSSMeasureMode.AT_MOST && availableHeight <= 0) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, Float.isNaN(availableWidth) ? 0 : (availableWidth - marginAxisRow)); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); + return; + } + + // If we're being asked to use an exact width/height, there's no need to measure the children. + if (widthMeasureMode == CSSMeasureMode.EXACTLY && heightMeasureMode == CSSMeasureMode.EXACTLY) { + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); return; } } - boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.WRAP); - + // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM + int mainAxis = resolveAxis(getFlexDirection(node), direction); + int crossAxis = getCrossFlexDirection(mainAxis, direction); + boolean isMainAxisRow = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE); CSSJustify justifyContent = node.style.justifyContent; - - float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])); - float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])); - float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))); - float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); - - boolean isMainDimDefined = (!Float.isNaN(node.layout.dimensions[dim[mainAxis]]) && node.layout.dimensions[dim[mainAxis]] >= 0.0); - boolean isCrossDimDefined = (!Float.isNaN(node.layout.dimensions[dim[crossAxis]]) && node.layout.dimensions[dim[crossAxis]] >= 0.0); - boolean isMainRowDirection = (mainAxis == CSS_FLEX_DIRECTION_ROW || mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE); - - int i; - int ii; - CSSNode child; - int axis; + boolean isNodeFlexWrap = (node.style.flexWrap == CSSWrap.WRAP); CSSNode firstAbsoluteChild = null; CSSNode currentAbsoluteChild = null; - float definedMainDim = CSSConstants.UNDEFINED; - if (isMainDimDefined) { - definedMainDim = node.layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain; + float leadingPaddingAndBorderMain = (node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])); + float trailingPaddingAndBorderMain = (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); + float leadingPaddingAndBorderCross = (node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])); + float paddingAndBorderAxisMain = ((node.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))); + float paddingAndBorderAxisCross = ((node.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (node.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + node.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); + + CSSMeasureMode measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode; + CSSMeasureMode measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode; + + // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS + float availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow; + float availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn; + float availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight; + float availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth; + + // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM + CSSNode child; + int i; + float childWidth; + float childHeight; + CSSMeasureMode childWidthMeasureMode; + CSSMeasureMode childHeightMeasureMode; + for (i = 0; i < childCount; i++) { + child = node.getChildAt(i); + + if (performLayout) { + // Set the initial position (relative to the parent). + CSSDirection childDirection = resolveDirection(child, direction); + setPosition(child, childDirection); + } + + // Absolute-positioned children don't participate in flex layout. Add them + // to a list that we can process later. + if (child.style.positionType == CSSPositionType.ABSOLUTE) { + + // Store a private linked list of absolutely positioned children + // so that we can efficiently traverse them later. + if (firstAbsoluteChild == null) { + firstAbsoluteChild = child; + } + if (currentAbsoluteChild != null) { + currentAbsoluteChild.nextChild = child; + } + currentAbsoluteChild = child; + child.nextChild = null; + } else { + + if (isMainAxisRow && (child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + + // The width is definite, so use that as the flex basis. + child.layout.flexBasis = Math.max(child.style.dimensions[DIMENSION_WIDTH], ((child.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW])) + (child.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]) + child.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])))); + } else if (!isMainAxisRow && (child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + + // The height is definite, so use that as the flex basis. + child.layout.flexBasis = Math.max(child.style.dimensions[DIMENSION_HEIGHT], ((child.style.padding.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN])) + (child.style.padding.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]) + child.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])))); + } else if (!isFlexBasisAuto(child) && !Float.isNaN(availableInnerMainDim)) { + + // If the basis isn't 'auto', it is assumed to be zero. + child.layout.flexBasis = Math.max(0, ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))); + } else { + + // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis). + childWidth = CSSConstants.UNDEFINED; + childHeight = CSSConstants.UNDEFINED; + childWidthMeasureMode = CSSMeasureMode.UNDEFINED; + childHeightMeasureMode = CSSMeasureMode.UNDEFINED; + + if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = child.style.dimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.EXACTLY; + } + if ((child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = child.style.dimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.EXACTLY; + } + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && Float.isNaN(childWidth) && !Float.isNaN(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSSMeasureMode.AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node.style.overflow == CSSOverflow.HIDDEN) { + if (isMainAxisRow && Float.isNaN(childHeight) && !Float.isNaN(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSSMeasureMode.AT_MOST; + } + } + + // Measure the child + layoutNodeInternal(layoutContext, child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "measure"); + + child.layout.flexBasis = Math.max(isMainAxisRow ? child.layout.measuredDimensions[DIMENSION_WIDTH] : child.layout.measuredDimensions[DIMENSION_HEIGHT], ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])))); + } + } } - // We want to execute the next two loops one per line with flex-wrap - int startLine = 0; - int endLine = 0; - // int nextOffset = 0; - int alreadyComputedNextLayout = 0; - // We aggregate the total dimensions of the container in those two variables - float linesCrossDim = 0; - float linesMainDim = 0; - int linesCount = 0; - while (endLine < childCount) { - // Layout non flexible children and count children by type + // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES + + // Indexes of children that represent the first and last items in the line. + int startOfLineIndex = 0; + int endOfLineIndex = 0; + + // Number of lines. + int lineCount = 0; + + // Accumulated cross dimensions of all lines so far. + float totalLineCrossDim = 0; - // mainContentDim is accumulation of the dimensions and margin of all the - // non flexible children. This will be used in order to either set the - // dimensions of the node if none already exist, or to compute the - // remaining space left for the flexible children. - float mainContentDim = 0; + // Max main dimension of all the lines. + float maxLineMainDim = 0; - // There are three kind of children, non flexible, flexible and absolute. - // We need to know how many there are in order to distribute the space. - int flexibleChildrenCount = 0; - float totalFlexible = 0; - int nonFlexibleChildrenCount = 0; + while (endOfLineIndex < childCount) { + + // Number of items on the currently line. May be different than the difference + // between start and end indicates because we skip over absolute-positioned items. + int itemsOnLine = 0; - // Use the line loop to position children in the main axis for as long - // as they are using a simple stacking behaviour. Children that are - // immediately stacked in the initial loop will not be touched again - // in . - boolean isSimpleStackMain = - (isMainDimDefined && justifyContent == CSSJustify.FLEX_START) || - (!isMainDimDefined && justifyContent != CSSJustify.CENTER); - int firstComplexMain = (isSimpleStackMain ? childCount : startLine); + // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin + // of all the children on the current line. This will be used in order to + // either set the dimensions of the node if none already exist or to compute + // the remaining space left for the flexible children. + float sizeConsumedOnCurrentLine = 0; - // Use the initial line loop to position children in the cross axis for - // as long as they are relatively positioned with alignment STRETCH or - // FLEX_START. Children that are immediately stacked in the initial loop - // will not be touched again in . - boolean isSimpleStackCross = true; - int firstComplexCross = childCount; + float totalFlexGrowFactors = 0; + float totalFlexShrinkScaledFactors = 0; - CSSNode firstFlexChild = null; - CSSNode currentFlexChild = null; + i = startOfLineIndex; - float mainDim = leadingPaddingAndBorderMain; - float crossDim = 0; + // Maintain a linked list of the child nodes that can shrink and/or grow. + CSSNode firstRelativeChild = null; + CSSNode currentRelativeChild = null; - float maxWidth = CSSConstants.UNDEFINED; - float maxHeight = CSSConstants.UNDEFINED; - for (i = startLine; i < childCount; ++i) { + // Add items to the current line until it's full or we run out of items. + while (i < childCount) { child = node.getChildAt(i); - child.lineIndex = linesCount; + child.lineIndex = lineCount; - child.nextAbsoluteChild = null; - child.nextFlexChild = null; - - CSSAlign alignItem = getAlignItem(node, child); - - // Pre-fill cross axis dimensions when the child is using stretch before - // we call the recursive layout pass - if (alignItem == CSSAlign.STRETCH && - child.style.positionType == CSSPositionType.RELATIVE && - isCrossDimDefined && - !(!Float.isNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] >= 0.0)) { - child.layout.dimensions[dim[crossAxis]] = Math.max( - boundAxis(child, crossAxis, node.layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))), - // You never want to go smaller than padding - ((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))) - ); - } else if (child.style.positionType == CSSPositionType.ABSOLUTE) { - // Store a private linked list of absolutely positioned children - // so that we can efficiently traverse them later. - if (firstAbsoluteChild == null) { - firstAbsoluteChild = child; + if (child.style.positionType != CSSPositionType.ABSOLUTE) { + float outerFlexBasis = child.layout.flexBasis + (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); + + // If this is a multi-line flow and this item pushes us over the available size, we've + // hit the end of the current line. Break out of the loop and lay out the current line. + if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) { + break; } - if (currentAbsoluteChild != null) { - currentAbsoluteChild.nextAbsoluteChild = child; - } - currentAbsoluteChild = child; - // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both - // left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; - if ((!Float.isNaN(node.layout.dimensions[dim[axis]]) && node.layout.dimensions[dim[axis]] >= 0.0) && - !(!Float.isNaN(child.style.dimensions[dim[axis]]) && child.style.dimensions[dim[axis]] >= 0.0) && - !Float.isNaN(child.style.position[leading[axis]]) && - !Float.isNaN(child.style.position[trailing[axis]])) { - child.layout.dimensions[dim[axis]] = Math.max( - boundAxis(child, axis, node.layout.dimensions[dim[axis]] - - ((node.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (node.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) - - (child.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) - - (Float.isNaN(child.style.position[leading[axis]]) ? 0 : child.style.position[leading[axis]]) - - (Float.isNaN(child.style.position[trailing[axis]]) ? 0 : child.style.position[trailing[axis]])), - // You never want to go smaller than padding - ((child.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + child.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (child.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + child.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) - ); - } + sizeConsumedOnCurrentLine += outerFlexBasis; + itemsOnLine++; + + if ((child.style.positionType == CSSPositionType.RELATIVE && child.style.flex != 0)) { + totalFlexGrowFactors += getFlexGrowFactor(child); + + // Unlike the grow factor, the shrink factor is scaled relative to the child + // dimension. + totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis; } + + // Store a private linked list of children that need to be layed out. + if (firstRelativeChild == null) { + firstRelativeChild = child; + } + if (currentRelativeChild != null) { + currentRelativeChild.nextChild = child; + } + currentRelativeChild = child; + child.nextChild = null; } - - float nextContentDim = 0; - - // It only makes sense to consider a child flexible if we have a computed - // dimension for the node. - if (isMainDimDefined && (child.style.positionType == CSSPositionType.RELATIVE && child.style.flex > 0)) { - flexibleChildrenCount++; - totalFlexible += child.style.flex; - - // Store a private linked list of flexible children so that we can - // efficiently traverse them later. - if (firstFlexChild == null) { - firstFlexChild = child; - } - if (currentFlexChild != null) { - currentFlexChild.nextFlexChild = child; - } - currentFlexChild = child; - - // Even if we don't know its exact size yet, we already know the padding, - // border and margin. We'll use this partial information, which represents - // the smallest possible size for the child, to compute the remaining - // available space. - nextContentDim = ((child.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (child.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + child.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) + - (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - - } else { - maxWidth = CSSConstants.UNDEFINED; - maxHeight = CSSConstants.UNDEFINED; - - if (!isMainRowDirection) { - if ((!Float.isNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0)) { - maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else { - maxWidth = parentMaxWidth - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) - - paddingAndBorderAxisResolvedRow; - } - } else { - if ((!Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else { - maxHeight = parentMaxHeight - - (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) - - paddingAndBorderAxisColumn; - } - } - - // This is the main recursive call. We layout non flexible children. - if (alreadyComputedNextLayout == 0) { - layoutNode(layoutContext, child, maxWidth, maxHeight, direction); - } - - // Absolute positioned elements do not take part of the layout, so we - // don't use them to compute mainContentDim - if (child.style.positionType == CSSPositionType.RELATIVE) { - nonFlexibleChildrenCount++; - // At this point we know the final size and margin of the element. - nextContentDim = (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - } - } - - // The element we are about to add would make us go to the next line - if (isNodeFlexWrap && - isMainDimDefined && - mainContentDim + nextContentDim > definedMainDim && - // If there's only one element, then it's bigger than the content - // and needs its own line - i != startLine) { - nonFlexibleChildrenCount--; - alreadyComputedNextLayout = 1; - break; - } - - // Disable simple stacking in the main axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackMain && - (child.style.positionType != CSSPositionType.RELATIVE || (child.style.positionType == CSSPositionType.RELATIVE && child.style.flex > 0))) { - isSimpleStackMain = false; - firstComplexMain = i; - } - - // Disable simple stacking in the cross axis for the current line as - // we found a non-trivial child. The remaining children will be laid out - // in . - if (isSimpleStackCross && - (child.style.positionType != CSSPositionType.RELATIVE || - (alignItem != CSSAlign.STRETCH && alignItem != CSSAlign.FLEX_START) || - (alignItem == CSSAlign.STRETCH && !isCrossDimDefined))) { - isSimpleStackCross = false; - firstComplexCross = i; - } - - if (isSimpleStackMain) { - child.layout.position[pos[mainAxis]] += mainDim; - if (isMainDimDefined) { - child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]]; - } - - mainDim += (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - crossDim = Math.max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))); - } - - if (isSimpleStackCross) { - child.layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross; - if (isCrossDimDefined) { - child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]]; - } - } - - alreadyComputedNextLayout = 0; - mainContentDim += nextContentDim; - endLine = i + 1; + + i++; + endOfLineIndex++; } - - // Layout flexible children and allocate empty space + + // If we don't need to measure the cross axis, we can skip the entire flex step. + boolean canSkipFlex = !performLayout && measureModeCrossDim == CSSMeasureMode.EXACTLY; // In order to position the elements in the main axis, we have two // controls. The space between the beginning and the first element @@ -576,212 +737,300 @@ public class LayoutEngine { float leadingMainDim = 0; float betweenMainDim = 0; - // The remaining available space that needs to be allocated - float remainingMainDim = 0; - if (isMainDimDefined) { - remainingMainDim = definedMainDim - mainContentDim; - } else { - remainingMainDim = Math.max(mainContentDim, 0) - mainContentDim; + // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS + // Calculate the remaining available space that needs to be allocated. + // If the main dimension size isn't known, it is computed based on + // the line length, so there's no more space left to distribute. + float remainingFreeSpace = 0; + if (!Float.isNaN(availableInnerMainDim)) { + remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine; + } else if (sizeConsumedOnCurrentLine < 0) { + // availableInnerMainDim is indefinite which means the node is being sized based on its content. + // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for + // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine. + remainingFreeSpace = -sizeConsumedOnCurrentLine; + } + + float remainingFreeSpaceAfterFlex = remainingFreeSpace; + + if (!canSkipFlex) { + float childFlexBasis; + float flexShrinkScaledFactor; + float flexGrowFactor; + float baseMainSize; + float boundMainSize; + + // Do two passes over the flex items to figure out how to distribute the remaining space. + // The first pass finds the items whose min/max constraints trigger, freezes them at those + // sizes, and excludes those sizes from the remaining space. The second pass sets the size + // of each flexible item. It distributes the remaining space amongst the items whose min/max + // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing + // their min/max constraints to trigger again. + // + // This two pass approach for resolving min/max constraints deviates from the spec. The + // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process + // that needs to be repeated a variable number of times. The algorithm implemented here + // won't handle all cases but it was simpler to implement and it mitigates performance + // concerns because we know exactly how many passes it'll do. + + // First pass: detect the flex items whose min/max constraints trigger + float deltaFreeSpace = 0; + float deltaFlexShrinkScaledFactors = 0; + float deltaFlexGrowFactors = 0; + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != null) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; + } + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + baseMainSize = childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor; + boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize); + if (baseMainSize != boundMainSize) { + // By excluding this item's size and flex factor from remaining, this item's + // min/max constraints should also trigger in the second pass resulting in the + // item's size calculation being identical in the first and second passes. + deltaFreeSpace -= boundMainSize; + deltaFlexGrowFactors -= flexGrowFactor; + } + } + } + + currentRelativeChild = currentRelativeChild.nextChild; + } + + totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; + totalFlexGrowFactors += deltaFlexGrowFactors; + remainingFreeSpace += deltaFreeSpace; + remainingFreeSpaceAfterFlex = remainingFreeSpace; + + // Second pass: resolve the sizes of the flexible items + currentRelativeChild = firstRelativeChild; + while (currentRelativeChild != null) { + childFlexBasis = currentRelativeChild.layout.flexBasis; + float updatedMainSize = childFlexBasis; + + if (remainingFreeSpace < 0) { + flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis; + + // Is this child able to shrink? + if (flexShrinkScaledFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor); + } + } else if (remainingFreeSpace > 0) { + flexGrowFactor = getFlexGrowFactor(currentRelativeChild); + + // Is this child able to grow? + if (flexGrowFactor != 0) { + updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis + + remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor); + } + } + + remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + + if (isMainAxisRow) { + childWidth = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.EXACTLY; + + if (!(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = availableInnerCrossDim; + childHeightMeasureMode = Float.isNaN(childHeight) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.AT_MOST; + } else { + childHeight = currentRelativeChild.style.dimensions[DIMENSION_HEIGHT] + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.EXACTLY; + } + } else { + childHeight = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + childHeightMeasureMode = CSSMeasureMode.EXACTLY; + + if (!(currentRelativeChild.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = availableInnerCrossDim; + childWidthMeasureMode = Float.isNaN(childWidth) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.AT_MOST; + } else { + childWidth = currentRelativeChild.style.dimensions[DIMENSION_WIDTH] + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childWidthMeasureMode = CSSMeasureMode.EXACTLY; + } + } + + boolean requiresStretchLayout = !(currentRelativeChild.style.dimensions[dim[crossAxis]] >= 0.0) && + getAlignItem(node, currentRelativeChild) == CSSAlign.STRETCH; + + // Recursively call the layout algorithm for this child with the updated main size. + layoutNodeInternal(layoutContext, currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, "flex"); + + currentRelativeChild = currentRelativeChild.nextChild; + } + } + + remainingFreeSpace = remainingFreeSpaceAfterFlex; + + // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION + + // At this point, all the children have their dimensions set in the main axis. + // Their dimensions are also set in the cross axis with the exception of items + // that are aligned "stretch". We need to compute these stretch values and + // set the final positions. + + // If we are using "at most" rules in the main axis, we won't distribute + // any remaining space at this point. + if (measureModeMainDim == CSSMeasureMode.AT_MOST) { + remainingFreeSpace = 0; } - // If there are flexible children in the mix, they are going to fill the - // remaining space - if (flexibleChildrenCount != 0) { - float flexibleMainDim = remainingMainDim / totalFlexible; - float baseMainDim; - float boundMainDim; - - // If the flex share of remaining space doesn't meet min/max bounds, - // remove this child from flex calculations. - currentFlexChild = firstFlexChild; - while (currentFlexChild != null) { - baseMainDim = flexibleMainDim * currentFlexChild.style.flex + - ((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))); - boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim); - - if (baseMainDim != boundMainDim) { - remainingMainDim -= boundMainDim; - totalFlexible -= currentFlexChild.style.flex; - } - - currentFlexChild = currentFlexChild.nextFlexChild; - } - flexibleMainDim = remainingMainDim / totalFlexible; - - // The non flexible children can overflow the container, in this case - // we should just assume that there is no space available. - if (flexibleMainDim < 0) { - flexibleMainDim = 0; - } - - currentFlexChild = firstFlexChild; - while (currentFlexChild != null) { - // At this point we know the final size of the element in the main - // dimension - currentFlexChild.layout.dimensions[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis, - flexibleMainDim * currentFlexChild.style.flex + - ((currentFlexChild.style.padding.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + currentFlexChild.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis])) + (currentFlexChild.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + currentFlexChild.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))) - ); - - maxWidth = CSSConstants.UNDEFINED; - if ((!Float.isNaN(node.layout.dimensions[dim[resolvedRowAxis]]) && node.layout.dimensions[dim[resolvedRowAxis]] >= 0.0)) { - maxWidth = node.layout.dimensions[dim[resolvedRowAxis]] - - paddingAndBorderAxisResolvedRow; - } else if (!isMainRowDirection) { - maxWidth = parentMaxWidth - - (node.style.margin.getWithFallback(leadingSpacing[resolvedRowAxis], leading[resolvedRowAxis]) + node.style.margin.getWithFallback(trailingSpacing[resolvedRowAxis], trailing[resolvedRowAxis])) - - paddingAndBorderAxisResolvedRow; - } - maxHeight = CSSConstants.UNDEFINED; - if ((!Float.isNaN(node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]) && node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { - maxHeight = node.layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - - paddingAndBorderAxisColumn; - } else if (isMainRowDirection) { - maxHeight = parentMaxHeight - - (node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) - - paddingAndBorderAxisColumn; - } - - // And we recursively call the layout algorithm for this child - layoutNode(layoutContext, currentFlexChild, maxWidth, maxHeight, direction); - - child = currentFlexChild; - currentFlexChild = currentFlexChild.nextFlexChild; - child.nextFlexChild = null; - } - - // We use justifyContent to figure out how to allocate the remaining - // space available - } else if (justifyContent != CSSJustify.FLEX_START) { + // Use justifyContent to figure out how to allocate the remaining space + // available in the main axis. + if (justifyContent != CSSJustify.FLEX_START) { if (justifyContent == CSSJustify.CENTER) { - leadingMainDim = remainingMainDim / 2; + leadingMainDim = remainingFreeSpace / 2; } else if (justifyContent == CSSJustify.FLEX_END) { - leadingMainDim = remainingMainDim; + leadingMainDim = remainingFreeSpace; } else if (justifyContent == CSSJustify.SPACE_BETWEEN) { - remainingMainDim = Math.max(remainingMainDim, 0); - if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) { - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount - 1); + remainingFreeSpace = Math.max(remainingFreeSpace, 0); + if (itemsOnLine > 1) { + betweenMainDim = remainingFreeSpace / (itemsOnLine - 1); } else { betweenMainDim = 0; } } else if (justifyContent == CSSJustify.SPACE_AROUND) { // Space on the edges is half of the space between elements - betweenMainDim = remainingMainDim / - (flexibleChildrenCount + nonFlexibleChildrenCount); + betweenMainDim = remainingFreeSpace / itemsOnLine; leadingMainDim = betweenMainDim / 2; } } - // Position elements in the main axis and compute dimensions + float mainDim = leadingPaddingAndBorderMain + leadingMainDim; + float crossDim = 0; - // At this point, all the children have their dimensions set. We need to - // find their position. In order to do that, we accumulate data in - // variables that are also useful to compute the total dimensions of the - // container! - mainDim += leadingMainDim; - - for (i = firstComplexMain; i < endLine; ++i) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { child = node.getChildAt(i); if (child.style.positionType == CSSPositionType.ABSOLUTE && !Float.isNaN(child.style.position[leading[mainAxis]])) { - // In case the child is position absolute and has left/top being - // defined, we override the position to whatever the user said - // (and margin/border). - child.layout.position[pos[mainAxis]] = (Float.isNaN(child.style.position[leading[mainAxis]]) ? 0 : child.style.position[leading[mainAxis]]) + - node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + - child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]); - } else { - // If the child is position absolute (without top/left) or relative, - // we put it at the current accumulated offset. - child.layout.position[pos[mainAxis]] += mainDim; - - // Define the trailing position accordingly. - if (isMainDimDefined) { - child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]]; + if (performLayout) { + // In case the child is position absolute and has left/top being + // defined, we override the position to whatever the user said + // (and margin/border). + child.layout.position[pos[mainAxis]] = (Float.isNaN(child.style.position[leading[mainAxis]]) ? 0 : child.style.position[leading[mainAxis]]) + + node.style.border.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]); } - - // Now that we placed the element, we need to update the variables - // We only need to do that for relative elements. Absolute elements + } else { + if (performLayout) { + // If the child is position absolute (without top/left) or relative, + // we put it at the current accumulated offset. + child.layout.position[pos[mainAxis]] += mainDim; + } + + // Now that we placed the element, we need to update the variables. + // We need to do that only for relative elements. Absolute elements // do not take part in that phase. if (child.style.positionType == CSSPositionType.RELATIVE) { - // The main dimension is the sum of all the elements dimension plus - // the spacing. - mainDim += betweenMainDim + (child.layout.dimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); - // The cross dimension is the max of the elements dimension since there - // can only be one element in that cross dimension. - crossDim = Math.max(crossDim, boundAxis(child, crossAxis, (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])))); + if (canSkipFlex) { + // If we skipped the flex step, then we can't rely on the measuredDims because + // they weren't computed. This means we can't call getDimWithMargin. + mainDim += betweenMainDim + (child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])) + child.layout.flexBasis; + crossDim = availableInnerCrossDim; + } else { + // The main dimension is the sum of all the elements dimension plus + // the spacing. + mainDim += betweenMainDim + (child.layout.measuredDimensions[dim[mainAxis]] + child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis])); + + // The cross dimension is the max of the elements dimension since there + // can only be one element in that cross dimension. + crossDim = Math.max(crossDim, (child.layout.measuredDimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); + } } } } - float containerCrossAxis = node.layout.dimensions[dim[crossAxis]]; - if (!isCrossDimDefined) { - containerCrossAxis = Math.max( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); + mainDim += trailingPaddingAndBorderMain; + + float containerCrossAxis = availableInnerCrossDim; + if (measureModeCrossDim == CSSMeasureMode.UNDEFINED || measureModeCrossDim == CSSMeasureMode.AT_MOST) { + // Compute the cross axis from the max cross dimension of the children. + containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; + + if (measureModeCrossDim == CSSMeasureMode.AT_MOST) { + containerCrossAxis = Math.min(containerCrossAxis, availableInnerCrossDim); + } } - // Position elements in the cross axis - for (i = firstComplexCross; i < endLine; ++i) { - child = node.getChildAt(i); + // If there's no flex wrap, the cross dimension is defined by the container. + if (!isNodeFlexWrap && measureModeCrossDim == CSSMeasureMode.EXACTLY) { + crossDim = availableInnerCrossDim; + } - if (child.style.positionType == CSSPositionType.ABSOLUTE && - !Float.isNaN(child.style.position[leading[crossAxis]])) { - // In case the child is absolutely positionned and has a - // top/left/bottom/right being set, we override all the previously - // computed positions to set it correctly. - child.layout.position[pos[crossAxis]] = (Float.isNaN(child.style.position[leading[crossAxis]]) ? 0 : child.style.position[leading[crossAxis]]) + - node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + - child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + // Clamp to the min/max size specified on the container. + crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross; - } else { - float leadingCrossDim = leadingPaddingAndBorderCross; + // STEP 7: CROSS-AXIS ALIGNMENT + // We can skip child alignment if we're just measuring the container. + if (performLayout) { + for (i = startOfLineIndex; i < endOfLineIndex; ++i) { + child = node.getChildAt(i); - // For a relative children, we're either using alignItems (parent) or - // alignSelf (child) in order to determine the position in the cross axis - if (child.style.positionType == CSSPositionType.RELATIVE) { - /*eslint-disable */ - // This variable is intentionally re-defined as the code is transpiled to a block scope language + if (child.style.positionType == CSSPositionType.ABSOLUTE) { + // If the child is absolutely positioned and has a top/left/bottom/right + // set, override all the previously computed positions to set it correctly. + if (!Float.isNaN(child.style.position[leading[crossAxis]])) { + child.layout.position[pos[crossAxis]] = (Float.isNaN(child.style.position[leading[crossAxis]]) ? 0 : child.style.position[leading[crossAxis]]) + + node.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + } else { + child.layout.position[pos[crossAxis]] = leadingPaddingAndBorderCross + + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + } + } else { + float leadingCrossDim = leadingPaddingAndBorderCross; + + // For a relative children, we're either using alignItems (parent) or + // alignSelf (child) in order to determine the position in the cross axis CSSAlign alignItem = getAlignItem(node, child); - /*eslint-enable */ + + // If the child uses align stretch, we need to lay it out one more time, this time + // forcing the cross-axis size to be the computed cross size for the current line. if (alignItem == CSSAlign.STRETCH) { - // You can only stretch if the dimension has not already been defined - // previously. - if (!(!Float.isNaN(child.style.dimensions[dim[crossAxis]]) && child.style.dimensions[dim[crossAxis]] >= 0.0)) { - float dimCrossAxis = child.layout.dimensions[dim[crossAxis]]; - child.layout.dimensions[dim[crossAxis]] = Math.max( - boundAxis(child, crossAxis, containerCrossAxis - - paddingAndBorderAxisCross - (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))), - // You never want to go smaller than padding - ((child.style.padding.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.border.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis])) + (child.style.padding.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + child.style.border.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))) - ); - - // If the size has changed, and this child has children we need to re-layout this child - if (dimCrossAxis != child.layout.dimensions[dim[crossAxis]] && child.getChildCount() > 0) { - // Reset child margins before re-layout as they are added back in layoutNode and would be doubled - child.layout.position[leading[mainAxis]] -= child.style.margin.getWithFallback(leadingSpacing[mainAxis], leading[mainAxis]) + - getRelativePosition(child, mainAxis); - child.layout.position[trailing[mainAxis]] -= child.style.margin.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + - getRelativePosition(child, mainAxis); - child.layout.position[leading[crossAxis]] -= child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + - getRelativePosition(child, crossAxis); - child.layout.position[trailing[crossAxis]] -= child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) + - getRelativePosition(child, crossAxis); - - layoutNode(layoutContext, child, maxWidth, maxHeight, direction); - } + childWidth = child.layout.measuredDimensions[DIMENSION_WIDTH] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childHeight = child.layout.measuredDimensions[DIMENSION_HEIGHT] + (child.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + child.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + boolean isCrossSizeDefinite = false; + + if (isMainAxisRow) { + isCrossSizeDefinite = (child.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0); + childHeight = crossDim; + } else { + isCrossSizeDefinite = (child.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0); + childWidth = crossDim; + } + + // If the child defines a definite size for its cross axis, there's no need to stretch. + if (!isCrossSizeDefinite) { + childWidthMeasureMode = Float.isNaN(childWidth) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; + childHeightMeasureMode = Float.isNaN(childHeight) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; + layoutNodeInternal(layoutContext, child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, "stretch"); } } else if (alignItem != CSSAlign.FLEX_START) { - // The remaining space between the parent dimensions+padding and child - // dimensions+margin. - float remainingCrossDim = containerCrossAxis - - paddingAndBorderAxisCross - (child.layout.dimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])); + float remainingCrossDim = containerCrossAxis - (child.layout.measuredDimensions[dim[crossAxis]] + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])); if (alignItem == CSSAlign.CENTER) { leadingCrossDim += remainingCrossDim / 2; @@ -789,41 +1038,25 @@ public class LayoutEngine { leadingCrossDim += remainingCrossDim; } } - } - // And we apply the position - child.layout.position[pos[crossAxis]] += linesCrossDim + leadingCrossDim; - - // Define the trailing position accordingly. - if (isCrossDimDefined) { - child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]]; + // And we apply the position + child.layout.position[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim; } } } - linesCrossDim += crossDim; - linesMainDim = Math.max(linesMainDim, mainDim); - linesCount += 1; - startLine = endLine; + totalLineCrossDim += crossDim; + maxLineMainDim = Math.max(maxLineMainDim, mainDim); + + // Reset variables for new line. + lineCount++; + startOfLineIndex = endOfLineIndex; + endOfLineIndex = startOfLineIndex; } - // - // - // Note(prenaux): More than one line, we need to layout the crossAxis - // according to alignContent. - // - // Note that we could probably remove and handle the one line case - // here too, but for the moment this is safer since it won't interfere with - // previously working code. - // - // See specs: - // http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm - // section 9.4 - // - if (linesCount > 1 && isCrossDimDefined) { - float nodeCrossAxisInnerSize = node.layout.dimensions[dim[crossAxis]] - - paddingAndBorderAxisCross; - float remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim; + // STEP 8: MULTI-LINE CONTENT ALIGNMENT + if (lineCount > 1 && performLayout && !Float.isNaN(availableInnerCrossDim)) { + float remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim; float crossDimLead = 0; float currentLead = leadingPaddingAndBorderCross; @@ -834,53 +1067,54 @@ public class LayoutEngine { } else if (alignContent == CSSAlign.CENTER) { currentLead += remainingAlignContentDim / 2; } else if (alignContent == CSSAlign.STRETCH) { - if (nodeCrossAxisInnerSize > linesCrossDim) { - crossDimLead = (remainingAlignContentDim / linesCount); + if (availableInnerCrossDim > totalLineCrossDim) { + crossDimLead = (remainingAlignContentDim / lineCount); } } int endIndex = 0; - for (i = 0; i < linesCount; ++i) { + for (i = 0; i < lineCount; ++i) { int startIndex = endIndex; + int j; // compute the line's height and find the endIndex float lineHeight = 0; - for (ii = startIndex; ii < childCount; ++ii) { - child = node.getChildAt(ii); + for (j = startIndex; j < childCount; ++j) { + child = node.getChildAt(j); if (child.style.positionType != CSSPositionType.RELATIVE) { continue; } if (child.lineIndex != i) { break; } - if ((!Float.isNaN(child.layout.dimensions[dim[crossAxis]]) && child.layout.dimensions[dim[crossAxis]] >= 0.0)) { - lineHeight = Math.max( - lineHeight, - child.layout.dimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis])) - ); + if ((child.layout.measuredDimensions[dim[crossAxis]] >= 0.0)) { + lineHeight = Math.max(lineHeight, + child.layout.measuredDimensions[dim[crossAxis]] + (child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]) + child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]))); } } - endIndex = ii; + endIndex = j; lineHeight += crossDimLead; - for (ii = startIndex; ii < endIndex; ++ii) { - child = node.getChildAt(ii); - if (child.style.positionType != CSSPositionType.RELATIVE) { - continue; - } + if (performLayout) { + for (j = startIndex; j < endIndex; ++j) { + child = node.getChildAt(j); + if (child.style.positionType != CSSPositionType.RELATIVE) { + continue; + } - CSSAlign alignContentAlignItem = getAlignItem(node, child); - if (alignContentAlignItem == CSSAlign.FLEX_START) { - child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); - } else if (alignContentAlignItem == CSSAlign.FLEX_END) { - child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.dimensions[dim[crossAxis]]; - } else if (alignContentAlignItem == CSSAlign.CENTER) { - float childHeight = child.layout.dimensions[dim[crossAxis]]; - child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; - } else if (alignContentAlignItem == CSSAlign.STRETCH) { - child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); - // TODO(prenaux): Correctly set the height of items with undefined - // (auto) crossAxis dimension. + CSSAlign alignContentAlignItem = getAlignItem(node, child); + if (alignContentAlignItem == CSSAlign.FLEX_START) { + child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + } else if (alignContentAlignItem == CSSAlign.FLEX_END) { + child.layout.position[pos[crossAxis]] = currentLead + lineHeight - child.style.margin.getWithFallback(trailingSpacing[crossAxis], trailing[crossAxis]) - child.layout.measuredDimensions[dim[crossAxis]]; + } else if (alignContentAlignItem == CSSAlign.CENTER) { + childHeight = child.layout.measuredDimensions[dim[crossAxis]]; + child.layout.position[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2; + } else if (alignContentAlignItem == CSSAlign.STRETCH) { + child.layout.position[pos[crossAxis]] = currentLead + child.style.margin.getWithFallback(leadingSpacing[crossAxis], leading[crossAxis]); + // TODO(prenaux): Correctly set the height of items with indefinite + // (auto) crossAxis dimension. + } } } @@ -888,93 +1122,148 @@ public class LayoutEngine { } } - boolean needsMainTrailingPos = false; - boolean needsCrossTrailingPos = false; + // STEP 9: COMPUTING FINAL DIMENSIONS + node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); + node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - // If the user didn't specify a width or height, and it has not been set - // by the container, then we set it via the children. - if (!isMainDimDefined) { - node.layout.dimensions[dim[mainAxis]] = Math.max( - // We're missing the last padding at this point to get the final - // dimension - boundAxis(node, mainAxis, linesMainDim + (node.style.padding.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]) + node.style.border.getWithFallback(trailingSpacing[mainAxis], trailing[mainAxis]))), - // We can never assign a width smaller than the padding and borders - paddingAndBorderAxisMain - ); + // If the user didn't specify a width or height for the node, set the + // dimensions based on the children. + if (measureModeMainDim == CSSMeasureMode.UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout.measuredDimensions[dim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim); + } else if (measureModeMainDim == CSSMeasureMode.AT_MOST) { + node.layout.measuredDimensions[dim[mainAxis]] = Math.max( + Math.min(availableInnerMainDim + paddingAndBorderAxisMain, + boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)), + paddingAndBorderAxisMain); + } + + if (measureModeCrossDim == CSSMeasureMode.UNDEFINED) { + // Clamp the size to the min/max size, if specified, and make sure it + // doesn't go below the padding and border amount. + node.layout.measuredDimensions[dim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross); + } else if (measureModeCrossDim == CSSMeasureMode.AT_MOST) { + node.layout.measuredDimensions[dim[crossAxis]] = Math.max( + Math.min(availableInnerCrossDim + paddingAndBorderAxisCross, + boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)), + paddingAndBorderAxisCross); + } + + // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN + if (performLayout) { + boolean needsMainTrailingPos = false; + boolean needsCrossTrailingPos = false; if (mainAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || mainAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsMainTrailingPos = true; } - } - - if (!isCrossDimDefined) { - node.layout.dimensions[dim[crossAxis]] = Math.max( - // For the cross dim, we add both sides at the end because the value - // is aggregate via a max function. Intermediate negative values - // can mess this computation otherwise - boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross), - paddingAndBorderAxisCross - ); if (crossAxis == CSS_FLEX_DIRECTION_ROW_REVERSE || crossAxis == CSS_FLEX_DIRECTION_COLUMN_REVERSE) { needsCrossTrailingPos = true; } - } - // Set trailing position if necessary - if (needsMainTrailingPos || needsCrossTrailingPos) { - for (i = 0; i < childCount; ++i) { - child = node.getChildAt(i); + // Set trailing position if necessary. + if (needsMainTrailingPos || needsCrossTrailingPos) { + for (i = 0; i < childCount; ++i) { + child = node.getChildAt(i); - if (needsMainTrailingPos) { - child.layout.position[trailing[mainAxis]] = node.layout.dimensions[dim[mainAxis]] - child.layout.dimensions[dim[mainAxis]] - child.layout.position[pos[mainAxis]]; - } + if (needsMainTrailingPos) { + child.layout.position[trailing[mainAxis]] = node.layout.measuredDimensions[dim[mainAxis]] - (child.style.positionType == CSSPositionType.ABSOLUTE ? 0 : child.layout.measuredDimensions[dim[mainAxis]]) - child.layout.position[pos[mainAxis]]; + } - if (needsCrossTrailingPos) { - child.layout.position[trailing[crossAxis]] = node.layout.dimensions[dim[crossAxis]] - child.layout.dimensions[dim[crossAxis]] - child.layout.position[pos[crossAxis]]; + if (needsCrossTrailingPos) { + child.layout.position[trailing[crossAxis]] = node.layout.measuredDimensions[dim[crossAxis]] - (child.style.positionType == CSSPositionType.ABSOLUTE ? 0 : child.layout.measuredDimensions[dim[crossAxis]]) - child.layout.position[pos[crossAxis]]; + } } } } - - // Calculate dimensions for absolutely positioned elements + + // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN currentAbsoluteChild = firstAbsoluteChild; while (currentAbsoluteChild != null) { - // Pre-fill dimensions when using absolute position and both offsets for - // the axis are defined (either both left and right or top and bottom). - for (ii = 0; ii < 2; ii++) { - axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN; + // Now that we know the bounds of the container, perform layout again on the + // absolutely-positioned children. + if (performLayout) { - if ((!Float.isNaN(node.layout.dimensions[dim[axis]]) && node.layout.dimensions[dim[axis]] >= 0.0) && - !(!Float.isNaN(currentAbsoluteChild.style.dimensions[dim[axis]]) && currentAbsoluteChild.style.dimensions[dim[axis]] >= 0.0) && - !Float.isNaN(currentAbsoluteChild.style.position[leading[axis]]) && - !Float.isNaN(currentAbsoluteChild.style.position[trailing[axis]])) { - currentAbsoluteChild.layout.dimensions[dim[axis]] = Math.max( - boundAxis(currentAbsoluteChild, axis, node.layout.dimensions[dim[axis]] - - (node.style.border.getWithFallback(leadingSpacing[axis], leading[axis]) + node.style.border.getWithFallback(trailingSpacing[axis], trailing[axis])) - - (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[axis], trailing[axis])) - - (Float.isNaN(currentAbsoluteChild.style.position[leading[axis]]) ? 0 : currentAbsoluteChild.style.position[leading[axis]]) - - (Float.isNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]]) - ), - // You never want to go smaller than padding - ((currentAbsoluteChild.style.padding.getWithFallback(leadingSpacing[axis], leading[axis]) + currentAbsoluteChild.style.border.getWithFallback(leadingSpacing[axis], leading[axis])) + (currentAbsoluteChild.style.padding.getWithFallback(trailingSpacing[axis], trailing[axis]) + currentAbsoluteChild.style.border.getWithFallback(trailingSpacing[axis], trailing[axis]))) - ); + childWidth = CSSConstants.UNDEFINED; + childHeight = CSSConstants.UNDEFINED; + + if ((currentAbsoluteChild.style.dimensions[dim[CSS_FLEX_DIRECTION_ROW]] >= 0.0)) { + childWidth = currentAbsoluteChild.style.dimensions[DIMENSION_WIDTH] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + } else { + // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined. + if (!Float.isNaN(currentAbsoluteChild.style.position[POSITION_LEFT]) && !Float.isNaN(currentAbsoluteChild.style.position[POSITION_RIGHT])) { + childWidth = node.layout.measuredDimensions[DIMENSION_WIDTH] - + (node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])) - + (currentAbsoluteChild.style.position[POSITION_LEFT] + currentAbsoluteChild.style.position[POSITION_RIGHT]); + childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth); + } + } + + if ((currentAbsoluteChild.style.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] >= 0.0)) { + childHeight = currentAbsoluteChild.style.dimensions[DIMENSION_HEIGHT] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + } else { + // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined. + if (!Float.isNaN(currentAbsoluteChild.style.position[POSITION_TOP]) && !Float.isNaN(currentAbsoluteChild.style.position[POSITION_BOTTOM])) { + childHeight = node.layout.measuredDimensions[DIMENSION_HEIGHT] - + (node.style.border.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + node.style.border.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])) - + (currentAbsoluteChild.style.position[POSITION_TOP] + currentAbsoluteChild.style.position[POSITION_BOTTOM]); + childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight); + } } - if (!Float.isNaN(currentAbsoluteChild.style.position[trailing[axis]]) && - !!Float.isNaN(currentAbsoluteChild.style.position[leading[axis]])) { - currentAbsoluteChild.layout.position[leading[axis]] = - node.layout.dimensions[dim[axis]] - - currentAbsoluteChild.layout.dimensions[dim[axis]] - - (Float.isNaN(currentAbsoluteChild.style.position[trailing[axis]]) ? 0 : currentAbsoluteChild.style.position[trailing[axis]]); + // If we're still missing one or the other dimension, measure the content. + if (Float.isNaN(childWidth) || Float.isNaN(childHeight)) { + childWidthMeasureMode = Float.isNaN(childWidth) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; + childHeightMeasureMode = Float.isNaN(childHeight) ? CSSMeasureMode.UNDEFINED : CSSMeasureMode.EXACTLY; + + // According to the spec, if the main size is not definite and the + // child's inline axis is parallel to the main axis (i.e. it's + // horizontal), the child should be sized using "UNDEFINED" in + // the main size. Otherwise use "AT_MOST" in the cross axis. + if (!isMainAxisRow && Float.isNaN(childWidth) && !Float.isNaN(availableInnerWidth)) { + childWidth = availableInnerWidth; + childWidthMeasureMode = CSSMeasureMode.AT_MOST; + } + + // The W3C spec doesn't say anything about the 'overflow' property, + // but all major browsers appear to implement the following logic. + if (node.style.overflow == CSSOverflow.HIDDEN) { + if (isMainAxisRow && Float.isNaN(childHeight) && !Float.isNaN(availableInnerHeight)) { + childHeight = availableInnerHeight; + childHeightMeasureMode = CSSMeasureMode.AT_MOST; + } + } + + layoutNodeInternal(layoutContext, currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, "abs-measure"); + childWidth = currentAbsoluteChild.layout.measuredDimensions[DIMENSION_WIDTH] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); + childHeight = currentAbsoluteChild.layout.measuredDimensions[DIMENSION_HEIGHT] + (currentAbsoluteChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + currentAbsoluteChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN])); + } + + layoutNodeInternal(layoutContext, currentAbsoluteChild, childWidth, childHeight, direction, CSSMeasureMode.EXACTLY, CSSMeasureMode.EXACTLY, true, "abs-layout"); + + if (!Float.isNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]) && + !!Float.isNaN(currentAbsoluteChild.style.position[leading[CSS_FLEX_DIRECTION_ROW]])) { + currentAbsoluteChild.layout.position[leading[CSS_FLEX_DIRECTION_ROW]] = + node.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + currentAbsoluteChild.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_ROW]] - + (Float.isNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]) ? 0 : currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_ROW]]); + } + + if (!Float.isNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]) && + !!Float.isNaN(currentAbsoluteChild.style.position[leading[CSS_FLEX_DIRECTION_COLUMN]])) { + currentAbsoluteChild.layout.position[leading[CSS_FLEX_DIRECTION_COLUMN]] = + node.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + currentAbsoluteChild.layout.measuredDimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] - + (Float.isNaN(currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]) ? 0 : currentAbsoluteChild.style.position[trailing[CSS_FLEX_DIRECTION_COLUMN]]); } } - child = currentAbsoluteChild; - currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild; - child.nextAbsoluteChild = null; + currentAbsoluteChild = currentAbsoluteChild.nextChild; } - } /** END_GENERATED **/ + } } diff --git a/src/java/tests/com/facebook/csslayout/LayoutCachingTest.java b/src/java/tests/com/facebook/csslayout/LayoutCachingTest.java index 492571cf..0fa61088 100644 --- a/src/java/tests/com/facebook/csslayout/LayoutCachingTest.java +++ b/src/java/tests/com/facebook/csslayout/LayoutCachingTest.java @@ -43,7 +43,7 @@ public class LayoutCachingTest { root.addChildAt(c0, 0); root.addChildAt(c1, 1); c0.addChildAt(c0c0, 0); - + root.calculateLayout(layoutContext); assertTreeHasNewLayout(true, root); markLayoutAppliedForTree(root); diff --git a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java index 7958ffb9..54560437 100644 --- a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java +++ b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java @@ -4374,6 +4374,7 @@ public class LayoutEngineTest { node_1.context = "measureWithRatio2"; node_1 = node_0.getChildAt(1); node_1.style.flexDirection = CSSFlexDirection.ROW; + node_1.style.overflow = CSSOverflow.HIDDEN; node_1.style.dimensions[DIMENSION_HEIGHT] = 100; addChildren(node_1, 2); { @@ -4825,7 +4826,7 @@ public class LayoutEngineTest { node_1 = node_0.getChildAt(0); node_1.layout.position[POSITION_TOP] = 20; node_1.layout.position[POSITION_LEFT] = 20; - node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_WIDTH] = 60; node_1.layout.dimensions[DIMENSION_HEIGHT] = 36; addChildren(node_1, 1); { @@ -8391,7 +8392,7 @@ public class LayoutEngineTest { } } - test("should layout child whose cross axis is undefined and whose alignSelf is stretch", root_node, root_layout); + test("should layout child whose cross axis is null and whose alignSelf is stretch", root_node, root_layout); } @Test @@ -8486,6 +8487,1081 @@ public class LayoutEngineTest { @Test public void testCase188() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 25; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 25; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 25; + } + } + } + + test("should not shrink column node when there is space left over", root_node, root_layout); + } + + @Test + public void testCase189() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 200; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 200; + } + } + } + + test("should shrink column node when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase190() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 30; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 25; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 30; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 30; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 55; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + test("should not shrink column node with siblings when there is space left over", root_node, root_layout); + } + + @Test + public void testCase191() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 100; + node_2.style.dimensions[DIMENSION_HEIGHT] = 80; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 25; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 25; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 60; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 80; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 85; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 15; + } + } + + test("should shrink column node with siblings when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase192() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 30; + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 40; + node_1 = node_0.getChildAt(2); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 50; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 22.5f; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 22.5f; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 40; + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 62.5f; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 37.5f; + } + } + + test("should shrink column nodes proportional to their main size when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase193() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 25; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 25; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should not shrink visible row node when there is space left over", root_node, root_layout); + } + + @Test + public void testCase194() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 200; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 200; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should shrink visible row node when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase195() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 30; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 30; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 30; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 55; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should not shrink visible row node with siblings when there is space left over", root_node, root_layout); + } + + @Test + public void testCase196() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 80; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 60; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 80; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 85; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink visible row node with siblings when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase197() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 30; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 40; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 50; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 22.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 22.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 40; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 62.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 37.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink visible row nodes when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase198() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.overflow = CSSOverflow.HIDDEN; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 25; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 25; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should not shrink hidden row node when there is space left over", root_node, root_layout); + } + + @Test + public void testCase199() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.overflow = CSSOverflow.HIDDEN; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 200; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 200; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + } + + test("should shrink hidden row node when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase200() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.overflow = CSSOverflow.HIDDEN; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 30; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 30; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 30; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 55; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should not shrink hidden row node with siblings when there is space left over", root_node, root_layout); + } + + @Test + public void testCase201() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.overflow = CSSOverflow.HIDDEN; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.dimensions[DIMENSION_WIDTH] = 80; + node_2.style.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 60; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 80; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 85; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink hidden row node with siblings when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase202() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 100; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.overflow = CSSOverflow.HIDDEN; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 30; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.dimensions[DIMENSION_WIDTH] = 40; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.style.overflow = CSSOverflow.HIDDEN; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_WIDTH] = 50; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 100; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 22.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 22.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 40; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 62.5f; + node_1.layout.dimensions[DIMENSION_WIDTH] = 37.5f; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink hidden row nodes proportional to their main size when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase203() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 213; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flexDirection = CSSFlexDirection.ROW; + node_1.style.alignItems = CSSAlign.FLEX_START; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.setMeasureFunction(sTestMeasureFunction); + node_2.context = "loooooooooong with space"; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 213; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 172; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 172; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 18; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 197; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should not shrink text node with siblings when there is space left over", root_node, root_layout); + } + + @Test + public void testCase204() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 140; + node_0.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.dimensions[DIMENSION_WIDTH] = 25; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.style.flexDirection = CSSFlexDirection.ROW; + node_1.style.alignItems = CSSAlign.FLEX_START; + node_1.style.flex = -1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.style.flex = -1; + node_2.setMeasureFunction(sTestMeasureFunction); + node_2.context = "loooooooooong with space"; + } + node_1 = node_0.getChildAt(2); + node_1.style.dimensions[DIMENSION_WIDTH] = 15; + node_1.style.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 140; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_0, 3); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 25; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 25; + node_1.layout.dimensions[DIMENSION_WIDTH] = 100; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + addChildren(node_1, 1); + { + TestCSSNode node_2; + node_2 = node_1.getChildAt(0); + node_2.layout.position[POSITION_TOP] = 0; + node_2.layout.position[POSITION_LEFT] = 0; + node_2.layout.dimensions[DIMENSION_WIDTH] = 100; + node_2.layout.dimensions[DIMENSION_HEIGHT] = 36; + } + node_1 = node_0.getChildAt(2); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 125; + node_1.layout.dimensions[DIMENSION_WIDTH] = 15; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 100; + } + } + + test("should shrink text node with siblings when there is not any space left over", root_node, root_layout); + } + + @Test + public void testCase205() { TestCSSNode root_node = new TestCSSNode(); { diff --git a/src/transpile.js b/src/transpile.js index e2ffa16a..8d3fa24d 100644 --- a/src/transpile.js +++ b/src/transpile.js @@ -39,8 +39,7 @@ global.layoutTestUtils = { }; global.describe = function(name, cb) { - if (name === 'Layout' || - name === 'Layout alignContent') { + if (name.toLowerCase().indexOf('javascript only') === -1) { cb(); } }; @@ -175,6 +174,10 @@ function printLayout(test) { 'exactly': 'CSS_MEASURE_MODE_EXACTLY', 'at-most': 'CSS_MEASURE_MODE_AT_MOST' }); + addEnum(node, 'overflow', 'overflow', { + 'visible': 'CSS_OVERFLOW_VISIBLE', + 'hidden': 'CSS_OVERFLOW_HIDDEN' + }); addFloat(node, 'flex', 'flex'); addFloat(node, 'width', 'dimensions[CSS_WIDTH]'); addFloat(node, 'height', 'dimensions[CSS_HEIGHT]'); @@ -256,8 +259,13 @@ function printLayout(test) { function transpileAnnotatedJStoC(jsCode) { return jsCode + .replace(/'abs-layout'/g, '"abs-layout"') + .replace(/'abs-measure'/g, '"abs-measure"') + .replace(/'flex'/g, '"flex"') + .replace(/'measure'/g, '"measure"') + .replace(/'stretch'/g, '"stretch"') .replace('node.style.measure', 'node.measure') - .replace(/null/g, 'NULL') + .replace(/undefined/g, 'NULL') .replace(/\.children\.length/g, '.children_count') .replace(/\.width/g, '.dimensions[CSS_WIDTH]') .replace(/\.height/g, '.dimensions[CSS_HEIGHT]') @@ -266,23 +274,30 @@ function transpileAnnotatedJStoC(jsCode) { .replace(/\.minWidth/g, '.minDimensions[CSS_WIDTH]') .replace(/\.minHeight/g, '.minDimensions[CSS_HEIGHT]') .replace(/\.lineIndex/g, '.line_index') - .replace(/\.nextAbsoluteChild/g, '.next_absolute_child') - .replace(/\.nextFlexChild/g, '.next_flex_child') - .replace(/layout\[dim/g, 'layout.dimensions[dim') + .replace(/\.nextChild/g, '.next_child') + .replace(/\.flexBasis/g, '.flex_basis') .replace(/layout\[pos/g, 'layout.position[pos') .replace(/layout\[leading/g, 'layout.position[leading') .replace(/layout\[trailing/g, 'layout.position[trailing') + .replace(/layout\[measuredDim/g, 'layout.measured_dimensions[dim') + .replace(/layout\.measuredWidth/g, 'layout.measured_dimensions[CSS_WIDTH]') + .replace(/layout\.measuredHeight/g, 'layout.measured_dimensions[CSS_HEIGHT]') .replace(/style\[dim/g, 'style.dimensions[dim') + .replace(/style\[CSS_LEFT/g, 'style.position[CSS_LEFT') + .replace(/style\[CSS_TOP/g, 'style.position[CSS_TOP') + .replace(/style\[CSS_RIGHT/g, 'style.position[CSS_RIGHT') + .replace(/style\[CSS_BOTTOM/g, 'style.position[CSS_BOTTOM') .replace(/node.children\[i\]/g, 'node->get_child(node->context, i)') - .replace(/node.children\[ii\]/g, 'node->get_child(node->context, ii)') + .replace(/node.children\[j\]/g, 'node->get_child(node->context, j)') .replace(/node\./g, 'node->') .replace(/child\./g, 'child->') - .replace(/parent\./g, 'parent->') .replace(/currentAbsoluteChild\./g, 'currentAbsoluteChild->') - .replace(/currentFlexChild\./g, 'currentFlexChild->') + .replace(/currentRelativeChild\./g, 'currentRelativeChild->') .replace(/getPositionType\((.+?)\)/g, '$1->style.position_type') .replace(/getJustifyContent\((.+?)\)/g, '$1->style.justify_content') .replace(/getAlignContent\((.+?)\)/g, '$1->style.align_content') + .replace(/assert\((.+?),\s*'(.+?)'\);/g, 'assert($1); // $2') + .replace(/getOverflow\((.+?)\)/g, '$1->style.overflow') .replace(/var\/\*\(c\)!([^*]+)\*\//g, '$1') .replace(/var\/\*([^\/]+)\*\//g, '$1') .replace(/ === /g, ' == ') @@ -290,8 +305,7 @@ function transpileAnnotatedJStoC(jsCode) { .replace(/\n {2}/g, '\n') .replace(/\/\*\(c\)!([^*]+)\*\//g, '$1') .replace(/\/[*]!([^*]+)[*]\//g, '$1') - .replace(/\/\*\(java\)!([^*]+)\*\//g, '') - .split('\n').slice(1, -1).join('\n'); + .replace(/\/\*\(java\)!([^*]+)\*\//g, ''); } function makeConstDefs() { @@ -318,14 +332,18 @@ function generateFile(fileName, generatedContent) { fs.writeFileSync(fileName, content); } +// Extract the function body by trimming the first ('function layoutNode(...) {') and +// last ('}') lines. Also, start the function body with a blank line so that regexes +// that use \n to match the start of a line will match the actual first line. +var computeLayoutCode = [''].concat(computeLayout.toString().split('\n').slice(1, -1)).join('\n'); var allTestsInC = allTests.map(printLayout); generateFile(__dirname + '/__tests__/Layout-test.c', allTestsInC.join('\n\n')); generateFile(__dirname + '/Layout-test-utils.c', makeConstDefs()); -generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayout.toString())); -generateFile(__dirname + '/java/src/com/facebook/csslayout/LayoutEngine.java', JavaTranspiler.transpileLayoutEngine(computeLayout.toString())); +generateFile(__dirname + '/Layout.c', transpileAnnotatedJStoC(computeLayoutCode)); +generateFile(__dirname + '/java/src/com/facebook/csslayout/LayoutEngine.java', JavaTranspiler.transpileLayoutEngine(computeLayoutCode)); generateFile(__dirname + '/java/tests/com/facebook/csslayout/TestConstants.java', JavaTranspiler.transpileCConstDefs(makeConstDefs())); generateFile(__dirname + '/java/tests/com/facebook/csslayout/LayoutEngineTest.java', JavaTranspiler.transpileCTestsArray(allTestsInC)); -generateFile(__dirname + '/csharp/Facebook.CSSLayout/LayoutEngine.cs', CSharpTranspiler.transpileLayoutEngine(computeLayout.toString())); +generateFile(__dirname + '/csharp/Facebook.CSSLayout/LayoutEngine.cs', CSharpTranspiler.transpileLayoutEngine(computeLayoutCode)); generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/TestConstants.cs', CSharpTranspiler.transpileCConstDefs(makeConstDefs())); generateFile(__dirname + '/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs', CSharpTranspiler.transpileCTestsArray(allTestsInC)); From 8779d942ea61a3eb9aa0296536a0234f702fda82 Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Thu, 28 Apr 2016 17:07:34 -0700 Subject: [PATCH 2/4] Fix positioning of items with min/max width/height We found a case where a flexible item with a max width that was supposed to be centered was positioned in the wrong location. The problem was with our 2 pass approach for sizing flexible items with a min/max width/height. Items sized in the first pass were being double counted when calculating the remainingFreeSpace. Specifically, their sizes were being subtracted from remainingFreeSpace in both the first and second passes. I also noticed a second unrelated bug. We weren't correctly calculating deltaFreeSpace in the first pass. When calculating deltaFreeSpace, we need to take into account the flex basis like we do in the second pass. Consequently, in the first pass I changed this: deltaFreeSpace -= boundMainSize; To this: deltaFreeSpace -= boundMainSize - childFlexBasis; The problem there was that we'd end up double counting childFlexBasis in the remainingFreeSpace. --- dist/css-layout.h | 14 +- dist/css-layout.jar | Bin 16191 -> 16323 bytes dist/css-layout.js | 14 +- dist/css-layout.min.js | 2 +- dist/css-layout.min.js.map | 2 +- src/Layout.c | 14 +- src/Layout.js | 14 +- src/__tests__/Layout-test.c | 87 +++++++++ src/__tests__/Layout-test.js | 24 +++ .../LayoutEngineTest.cs | 165 ++++++++++++++---- src/csharp/Facebook.CSSLayout/LayoutEngine.cs | 14 +- .../com/facebook/csslayout/LayoutEngine.java | 14 +- .../facebook/csslayout/LayoutEngineTest.java | 165 ++++++++++++++---- 13 files changed, 411 insertions(+), 118 deletions(-) diff --git a/dist/css-layout.h b/dist/css-layout.h index 95eab64d..4e0e1511 100644 --- a/dist/css-layout.h +++ b/dist/css-layout.h @@ -1171,7 +1171,8 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab remainingFreeSpace = -sizeConsumedOnCurrentLine; } - float remainingFreeSpaceAfterFlex = remainingFreeSpace; + float originalRemainingFreeSpace = remainingFreeSpace; + float deltaFreeSpace = 0; if (!canSkipFlex) { float childFlexBasis; @@ -1194,7 +1195,6 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // concerns because we know exactly how many passes it'll do. // First pass: detect the flex items whose min/max constraints trigger - float deltaFreeSpace = 0; float deltaFlexShrinkScaledFactors = 0; float deltaFlexGrowFactors = 0; currentRelativeChild = firstRelativeChild; @@ -1213,7 +1213,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; } } @@ -1229,7 +1229,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexGrowFactors -= flexGrowFactor; } } @@ -1241,9 +1241,9 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; totalFlexGrowFactors += deltaFlexGrowFactors; remainingFreeSpace += deltaFreeSpace; - remainingFreeSpaceAfterFlex = remainingFreeSpace; // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0; currentRelativeChild = firstRelativeChild; while (currentRelativeChild != NULL) { childFlexBasis = currentRelativeChild->layout.flex_basis; @@ -1267,7 +1267,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab } } - remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + deltaFreeSpace -= updatedMainSize - childFlexBasis; if (isMainAxisRow) { childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); @@ -1303,7 +1303,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab } } - remainingFreeSpace = remainingFreeSpaceAfterFlex; + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION diff --git a/dist/css-layout.jar b/dist/css-layout.jar index 21cef567a1cd090fc626efb5b69189cfdcc76968..a13686964852c308a570cf5742ac435a08e608fc 100644 GIT binary patch delta 13152 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 PeZIervQR*Qf202oGq(X4 delta 13051 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 I{QqkI54gCY7XSbN diff --git a/dist/css-layout.js b/dist/css-layout.js index c5bf0095..43df9fae 100644 --- a/dist/css-layout.js +++ b/dist/css-layout.js @@ -935,7 +935,8 @@ var computeLayout = (function() { remainingFreeSpace = -sizeConsumedOnCurrentLine; } - var/*float*/ remainingFreeSpaceAfterFlex = remainingFreeSpace; + var/*float*/ originalRemainingFreeSpace = remainingFreeSpace; + var/*float*/ deltaFreeSpace = 0; if (!canSkipFlex) { var/*float*/ childFlexBasis; @@ -958,7 +959,6 @@ var computeLayout = (function() { // concerns because we know exactly how many passes it'll do. // First pass: detect the flex items whose min/max constraints trigger - var/*float*/ deltaFreeSpace = 0; var/*float*/ deltaFlexShrinkScaledFactors = 0; var/*float*/ deltaFlexGrowFactors = 0; currentRelativeChild = firstRelativeChild; @@ -977,7 +977,7 @@ var computeLayout = (function() { // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; } } @@ -993,7 +993,7 @@ var computeLayout = (function() { // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexGrowFactors -= flexGrowFactor; } } @@ -1005,9 +1005,9 @@ var computeLayout = (function() { totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; totalFlexGrowFactors += deltaFlexGrowFactors; remainingFreeSpace += deltaFreeSpace; - remainingFreeSpaceAfterFlex = remainingFreeSpace; // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0; currentRelativeChild = firstRelativeChild; while (currentRelativeChild !== undefined) { childFlexBasis = currentRelativeChild.layout.flexBasis; @@ -1031,7 +1031,7 @@ var computeLayout = (function() { } } - remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + deltaFreeSpace -= updatedMainSize - childFlexBasis; if (isMainAxisRow) { childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); @@ -1067,7 +1067,7 @@ var computeLayout = (function() { } } - remainingFreeSpace = remainingFreeSpaceAfterFlex; + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION diff --git a/dist/css-layout.min.js b/dist/css-layout.min.js index 86e0108f..721c379f 100644 --- a/dist/css-layout.min.js +++ b/dist/css-layout.min.js @@ -1,2 +1,2 @@ -!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.computeLayout=b()}(this,function(){var a=function(){function a(b){if(b.layout&&!b.isDirty||(b.layout={width:void 0,height:void 0,top:0,left:0,right:0,bottom:0}),b.style||(b.style={}),b.children||(b.children=[]),b.style.measure&&b.children&&b.children.length)throw new Error("Using custom measure function is supported only for leaf nodes.");return b.children.forEach(a),b}function b(a){return void 0===a||Number.isNaN(a)}function c(a){return a===ca||a===da}function d(a){return a===ea||a===fa}function e(a){return void 0===a.style.flex?0:a.style.flex}function f(a){return V?!0:e(a)<=0}function g(a){return e(a)>0?e(a):0}function h(a){if(V){if(0!==e(a))return 1}else if(e(a)<0)return 1;return 0}function i(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function j(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function k(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function l(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function m(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function n(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function o(a,b){return k(a,b)+m(a,b)}function p(a,b){return l(a,b)+n(a,b)}function q(a,b){return i(a,b)+j(a,b)}function r(a,b){return o(a,b)+p(a,b)}function s(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function t(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function u(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function v(a,b){if(b===ba){if(a===ca)return da;if(a===da)return ca}return a}function w(a,b){var c;return c=a.style.direction?a.style.direction:_,c===_&&(c=void 0===b?aa:b),c}function x(a){return a.style.flexDirection?a.style.flexDirection:ea}function y(a,b){return d(a)?v(ca,b):ea}function z(a){return a.style.position?a.style.position:pa}function A(a){return a.style.overflow?a.style.overflow:ra}function B(a){return z(a)===pa&&void 0!==a.style.flex&&0!==a.style.flex}function C(a){return"wrap"===a.style.flexWrap}function D(a,b){return a.layout[Aa[b]]+q(a,b)}function E(a,b){return void 0!==a.style[za[b]]&&a.style[za[b]]>=0}function F(a,b){return void 0!==a.layout[Aa[b]]&&a.layout[Aa[b]]>=0}function G(a,b){return void 0!==a.style[b]}function H(a){return void 0!==a.style.measure}function I(a,b){return void 0!==a.style[b]?a.style[b]:0}function J(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function K(a,b){return b>a?a:b}function L(a,b){return a>b?a:b}function M(a,b,c){return L(J(a,b,c),r(a,b))}function N(a,b,c){var d=z(b)===qa?0:b.layout[Aa[c]];b.layout[xa[c]]=a.layout[Aa[c]]-d-b.layout[ya[c]]}function O(a,b){return void 0!==a.style[wa[b]]?I(a,wa[b]):-I(a,xa[b])}function P(a,b){var c=v(x(a),b),d=y(c,b);a.layout[wa[c]]=i(a,c)+O(a,c),a.layout[xa[c]]=j(a,c)+O(a,c),a.layout[wa[d]]=i(a,d)+O(a,d),a.layout[xa[d]]=j(a,d)+O(a,d)}function Q(a,b){if(!a)throw new Error(b)}function R(a,d,e,k,l,O,R){Q(b(d)?l===ta:!0,"availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED"),Q(b(e)?O===ta:!0,"availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED");var T=r(a,ca),V=r(a,ea),W=q(a,ca),_=q(a,ea),aa=w(a,k);if(a.layout.direction=aa,H(a)){var ba=d-W-T,ra=e-_-V;if(l===ua&&O===ua)a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_);else if(0>=ba)a.layout.measuredWidth=M(a,ca,0),a.layout.measuredHeight=M(a,ea,0);else{var za=a.style.measure(ba,l,ra,O);a.layout.measuredWidth=M(a,ca,l===ta||l===va?za.width+T:d-W),a.layout.measuredHeight=M(a,ea,O===ta||O===va?za.height+V:e-_)}}else{var Ba=a.children.length;if(0===Ba)return a.layout.measuredWidth=M(a,ca,l===ta||l===va?T:d-W),void(a.layout.measuredHeight=M(a,ea,O===ta||O===va?V:e-_));if(!R){if(l===va&&0>=d&&O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,0));if(l===va&&0>=d)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,b(e)?0:e-_));if(O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,b(d)?0:d-W),void(a.layout.measuredHeight=M(a,ea,0));if(l===ua&&O===ua)return a.layout.measuredWidth=M(a,ca,d-W),void(a.layout.measuredHeight=M(a,ea,e-_))}var Ca,Da,Ea,Fa,Ga,Ha,Ia=v(x(a),aa),Ja=y(Ia,aa),Ka=c(Ia),La=s(a),Ma=C(a),Na=void 0,Oa=void 0,Pa=o(a,Ia),Qa=p(a,Ia),Ra=o(a,Ja),Sa=r(a,Ia),Ta=r(a,Ja),Ua=Ka?l:O,Va=Ka?O:l,Wa=d-W-T,Xa=e-_-V,Ya=Ka?Wa:Xa,Za=Ka?Xa:Wa;for(Da=0;Ba>Da;Da++){if(Ca=a.children[Da],R){var $a=w(Ca,aa);P(Ca,$a)}z(Ca)===qa?(void 0===Na&&(Na=Ca),void 0!==Oa&&(Oa.nextChild=Ca),Oa=Ca,Ca.nextChild=void 0):Ka&&E(Ca,ca)?Ca.layout.flexBasis=L(Ca.style.width,r(Ca,ca)):!Ka&&E(Ca,ea)?Ca.layout.flexBasis=L(Ca.style.height,r(Ca,ea)):f(Ca)||b(Ya)?(Ea=U,Fa=U,Ga=ta,Ha=ta,E(Ca,ca)&&(Ea=Ca.style.width+q(Ca,ca),Ga=ua),E(Ca,ea)&&(Fa=Ca.style.height+q(Ca,ea),Ha=ua),Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Ca,Ea,Fa,aa,Ga,Ha,!1,"measure"),Ca.layout.flexBasis=L(Ka?Ca.layout.measuredWidth:Ca.layout.measuredHeight,r(Ca,Ia))):Ca.layout.flexBasis=L(0,r(Ca,Ia))}for(var _a=0,ab=0,bb=0,cb=0,db=0;Ba>ab;){var eb=0,fb=0,gb=0,hb=0;Da=_a;for(var ib=void 0,jb=void 0;Ba>Da;){if(Ca=a.children[Da],Ca.lineIndex=bb,z(Ca)!==qa){var kb=Ca.layout.flexBasis+q(Ca,Ia);if(fb+kb>Ya&&Ma&&eb>0)break;fb+=kb,eb++,B(Ca)&&(gb+=g(Ca),hb+=h(Ca)*Ca.layout.flexBasis),void 0===ib&&(ib=Ca),void 0!==jb&&(jb.nextChild=Ca),jb=Ca,Ca.nextChild=void 0}Da++,ab++}var lb=!R&&Va===ua,mb=0,nb=0,ob=0;b(Ya)?0>fb&&(ob=-fb):ob=Ya-fb;var pb=ob;if(!lb){var qb,rb,sb,tb,ub,vb=0,wb=0,xb=0;for(jb=ib;void 0!==jb;)qb=jb.layout.flexBasis,0>ob?(rb=h(jb)*qb,0!==rb&&(tb=qb+ob/hb*rb,ub=M(jb,Ia,tb),tb!==ub&&(vb-=ub,wb-=rb))):ob>0&&(sb=g(jb),0!==sb&&(tb=qb+ob/gb*sb,ub=M(jb,Ia,tb),tb!==ub&&(vb-=ub,xb-=sb))),jb=jb.nextChild;for(hb+=wb,gb+=xb,ob+=vb,pb=ob,jb=ib;void 0!==jb;){qb=jb.layout.flexBasis;var yb=qb;0>ob?(rb=h(jb)*qb,0!==rb&&(yb=M(jb,Ia,qb+ob/hb*rb))):ob>0&&(sb=g(jb),0!==sb&&(yb=M(jb,Ia,qb+ob/gb*sb))),pb-=yb-qb,Ka?(Ea=yb+q(jb,ca),Ga=ua,E(jb,ea)?(Fa=jb.style.height+q(jb,ea),Ha=ua):(Fa=Za,Ha=b(Fa)?ta:va)):(Fa=yb+q(jb,ea),Ha=ua,E(jb,ca)?(Ea=jb.style.width+q(jb,ca),Ga=ua):(Ea=Za,Ga=b(Ea)?ta:va));var zb=!E(jb,Ja)&&u(a,jb)===oa;S(jb,Ea,Fa,aa,Ga,Ha,R&&!zb,"flex"),jb=jb.nextChild}}ob=pb,Ua===va&&(ob=0),La!==ga&&(La===ha?mb=ob/2:La===ia?mb=ob:La===ja?(ob=L(ob,0),nb=eb>1?ob/(eb-1):0):La===ka&&(nb=ob/eb,mb=nb/2));var Ab=Pa+mb,Bb=0;for(Da=_a;ab>Da;++Da)Ca=a.children[Da],z(Ca)===qa&&G(Ca,wa[Ia])?R&&(Ca.layout[ya[Ia]]=I(Ca,wa[Ia])+m(a,Ia)+i(Ca,Ia)):(R&&(Ca.layout[ya[Ia]]+=Ab),z(Ca)===pa&&(lb?(Ab+=nb+q(Ca,Ia)+Ca.layout.flexBasis,Bb=Za):(Ab+=nb+D(Ca,Ia),Bb=L(Bb,D(Ca,Ja)))));Ab+=Qa;var Cb=Za;if(Va!==ta&&Va!==va||(Cb=M(a,Ja,Bb+Ta)-Ta,Va===va&&(Cb=K(Cb,Za))),Ma||Va!==ua||(Bb=Za),Bb=M(a,Ja,Bb+Ta)-Ta,R)for(Da=_a;ab>Da;++Da)if(Ca=a.children[Da],z(Ca)===qa)G(Ca,wa[Ja])?Ca.layout[ya[Ja]]=I(Ca,wa[Ja])+m(a,Ja)+i(Ca,Ja):Ca.layout[ya[Ja]]=Ra+i(Ca,Ja);else{var Db=Ra,Eb=u(a,Ca);if(Eb===oa){Ea=Ca.layout.measuredWidth+q(Ca,ca),Fa=Ca.layout.measuredHeight+q(Ca,ea);var Fb=!1;Ka?(Fb=E(Ca,ea),Fa=Bb):(Fb=E(Ca,ca),Ea=Bb),Fb||(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,S(Ca,Ea,Fa,aa,Ga,Ha,!0,"stretch"))}else if(Eb!==la){var Gb=Cb-D(Ca,Ja);Db+=Eb===ma?Gb/2:Gb}Ca.layout[ya[Ja]]+=cb+Db}cb+=Bb,db=L(db,Ab),bb++,_a=ab,ab=_a}if(bb>1&&R&&!b(Za)){var Hb=Za-cb,Ib=0,Jb=Ra,Kb=t(a);Kb===na?Jb+=Hb:Kb===ma?Jb+=Hb/2:Kb===oa&&Za>cb&&(Ib=Hb/bb);var Lb=0;for(Da=0;bb>Da;++Da){var Mb,Nb=Lb,Ob=0;for(Mb=Nb;Ba>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){if(Ca.lineIndex!==Da)break;F(Ca,Ja)&&(Ob=L(Ob,Ca.layout[Aa[Ja]]+q(Ca,Ja)))}if(Lb=Mb,Ob+=Ib,R)for(Mb=Nb;Lb>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){var Pb=u(a,Ca);Pb===la?Ca.layout[ya[Ja]]=Jb+i(Ca,Ja):Pb===na?Ca.layout[ya[Ja]]=Jb+Ob-j(Ca,Ja)-Ca.layout[Aa[Ja]]:Pb===ma?(Fa=Ca.layout[Aa[Ja]],Ca.layout[ya[Ja]]=Jb+(Ob-Fa)/2):Pb===oa&&(Ca.layout[ya[Ja]]=Jb+i(Ca,Ja))}Jb+=Ob}}if(a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_),Ua===ta?a.layout[Aa[Ia]]=M(a,Ia,db):Ua===va&&(a.layout[Aa[Ia]]=L(K(Ya+Sa,J(a,Ia,db)),Sa)),Va===ta?a.layout[Aa[Ja]]=M(a,Ja,cb+Ta):Va===va&&(a.layout[Aa[Ja]]=L(K(Za+Ta,J(a,Ja,cb+Ta)),Ta)),R){var Qb=!1,Rb=!1;if(Ia!==da&&Ia!==fa||(Qb=!0),Ja!==da&&Ja!==fa||(Rb=!0),Qb||Rb)for(Da=0;Ba>Da;++Da)Ca=a.children[Da],Qb&&N(a,Ca,Ia),Rb&&N(a,Ca,Ja)}for(Oa=Na;void 0!==Oa;)R&&(Ea=U,Fa=U,E(Oa,ca)?Ea=Oa.style.width+q(Oa,ca):G(Oa,X)&&G(Oa,Z)&&(Ea=a.layout.measuredWidth-(m(a,ca)+n(a,ca))-(Oa.style[X]+Oa.style[Z]),Ea=M(Oa,ca,Ea)),E(Oa,ea)?Fa=Oa.style.height+q(Oa,ea):G(Oa,Y)&&G(Oa,$)&&(Fa=a.layout.measuredHeight-(m(a,ea)+n(a,ea))-(Oa.style[Y]+Oa.style[$]),Fa=M(Oa,ea,Fa)),(b(Ea)||b(Fa))&&(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Oa,Ea,Fa,aa,Ga,Ha,!1,"abs-measure"),Ea=Oa.layout.measuredWidth+q(Oa,ca),Fa=Oa.layout.measuredHeight+q(Oa,ea)),S(Oa,Ea,Fa,aa,ua,ua,!0,"abs-layout"),G(Oa,xa[ca])&&!G(Oa,wa[ca])&&(Oa.layout[wa[ca]]=a.layout[Aa[ca]]-Oa.layout[Aa[ca]]-I(Oa,xa[ca])),G(Oa,xa[ea])&&!G(Oa,wa[ea])&&(Oa.layout[wa[ea]]=a.layout[Aa[ea]]-Oa.layout[Aa[ea]]-I(Oa,xa[ea]))),Oa=Oa.nextChild}}function S(a,b,c,d,e,f,g,h){var i=a.layout,j=a.isDirty&&i.generationCount!==W||i.lastParentDirection!==d;j&&(void 0!==i.cachedMeasurements&&(i.cachedMeasurements=[]),void 0!==i.cachedLayout&&(i.cachedLayout.widthMeasureMode=void 0,i.cachedLayout.heightMeasureMode=void 0));var k;if(g)i.cachedLayout&&i.cachedLayout.availableWidth===b&&i.cachedLayout.availableHeight===c&&i.cachedLayout.widthMeasureMode===e&&i.cachedLayout.heightMeasureMode===f&&(k=i.cachedLayout);else if(i.cachedMeasurements)for(var l=0,m=i.cachedMeasurements.length;m>l;l++)if(i.cachedMeasurements[l].availableWidth===b&&i.cachedMeasurements[l].availableHeight===c&&i.cachedMeasurements[l].widthMeasureMode===e&&i.cachedMeasurements[l].heightMeasureMode===f){k=i.cachedMeasurements[l];break}if(j||void 0===k){if(R(a,b,c,d,e,f,g),i.lastParentDirection=d,void 0===k){var n;g?(void 0===i.cachedLayout&&(i.cachedLayout={}),n=i.cachedLayout):(void 0===i.cachedMeasurements&&(i.cachedMeasurements=[]),n={},i.cachedMeasurements.push(n)),n.availableWidth=b,n.availableHeight=c,n.widthMeasureMode=e,n.heightMeasureMode=f,n.computedWidth=i.measuredWidth,n.computedHeight=i.measuredHeight}}else i.measureWidth=k.computedWidth,i.measureHeight=k.computedHeight;return g&&(a.layout.width=a.layout.measuredWidth,a.layout.height=a.layout.measuredHeight,i.shouldUpdate=!0),i.generationCount=W,j||void 0===k}function T(a,c,d,e){W++,b(c)&&E(a,ca)&&(c=a.style.width+q(a,ca)),b(d)&&E(a,ea)&&(d=a.style.height+q(a,ea));var f=b(c)?ta:ua,g=b(d)?ta:ua;S(a,c,d,e,f,g,!0,"initial")&&P(a,a.layout.direction)}var U,V=!1,W=0,X="left",Y="top",Z="right",$="bottom",_="inherit",aa="ltr",ba="rtl",ca="row",da="row-reverse",ea="column",fa="column-reverse",ga="flex-start",ha="center",ia="flex-end",ja="space-between",ka="space-around",la="flex-start",ma="center",na="flex-end",oa="stretch",pa="relative",qa="absolute",ra="visible",sa="hidden",ta="undefined",ua="exactly",va="at-most",wa={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},xa={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},ya={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},za={row:"width","row-reverse":"width",column:"height","column-reverse":"height"},Aa={row:"measuredWidth","row-reverse":"measuredWidth",column:"measuredHeight","column-reverse":"measuredHeight"};return{layoutNodeImpl:R,computeLayout:T,fillNodes:a}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); +!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.computeLayout=b()}(this,function(){var a=function(){function a(b){if(b.layout&&!b.isDirty||(b.layout={width:void 0,height:void 0,top:0,left:0,right:0,bottom:0}),b.style||(b.style={}),b.children||(b.children=[]),b.style.measure&&b.children&&b.children.length)throw new Error("Using custom measure function is supported only for leaf nodes.");return b.children.forEach(a),b}function b(a){return void 0===a||Number.isNaN(a)}function c(a){return a===ca||a===da}function d(a){return a===ea||a===fa}function e(a){return void 0===a.style.flex?0:a.style.flex}function f(a){return V?!0:e(a)<=0}function g(a){return e(a)>0?e(a):0}function h(a){if(V){if(0!==e(a))return 1}else if(e(a)<0)return 1;return 0}function i(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function j(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function k(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function l(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function m(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function n(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function o(a,b){return k(a,b)+m(a,b)}function p(a,b){return l(a,b)+n(a,b)}function q(a,b){return i(a,b)+j(a,b)}function r(a,b){return o(a,b)+p(a,b)}function s(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function t(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function u(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function v(a,b){if(b===ba){if(a===ca)return da;if(a===da)return ca}return a}function w(a,b){var c;return c=a.style.direction?a.style.direction:_,c===_&&(c=void 0===b?aa:b),c}function x(a){return a.style.flexDirection?a.style.flexDirection:ea}function y(a,b){return d(a)?v(ca,b):ea}function z(a){return a.style.position?a.style.position:pa}function A(a){return a.style.overflow?a.style.overflow:ra}function B(a){return z(a)===pa&&void 0!==a.style.flex&&0!==a.style.flex}function C(a){return"wrap"===a.style.flexWrap}function D(a,b){return a.layout[Aa[b]]+q(a,b)}function E(a,b){return void 0!==a.style[za[b]]&&a.style[za[b]]>=0}function F(a,b){return void 0!==a.layout[Aa[b]]&&a.layout[Aa[b]]>=0}function G(a,b){return void 0!==a.style[b]}function H(a){return void 0!==a.style.measure}function I(a,b){return void 0!==a.style[b]?a.style[b]:0}function J(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function K(a,b){return b>a?a:b}function L(a,b){return a>b?a:b}function M(a,b,c){return L(J(a,b,c),r(a,b))}function N(a,b,c){var d=z(b)===qa?0:b.layout[Aa[c]];b.layout[xa[c]]=a.layout[Aa[c]]-d-b.layout[ya[c]]}function O(a,b){return void 0!==a.style[wa[b]]?I(a,wa[b]):-I(a,xa[b])}function P(a,b){var c=v(x(a),b),d=y(c,b);a.layout[wa[c]]=i(a,c)+O(a,c),a.layout[xa[c]]=j(a,c)+O(a,c),a.layout[wa[d]]=i(a,d)+O(a,d),a.layout[xa[d]]=j(a,d)+O(a,d)}function Q(a,b){if(!a)throw new Error(b)}function R(a,d,e,k,l,O,R){Q(b(d)?l===ta:!0,"availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED"),Q(b(e)?O===ta:!0,"availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED");var T=r(a,ca),V=r(a,ea),W=q(a,ca),_=q(a,ea),aa=w(a,k);if(a.layout.direction=aa,H(a)){var ba=d-W-T,ra=e-_-V;if(l===ua&&O===ua)a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_);else if(0>=ba)a.layout.measuredWidth=M(a,ca,0),a.layout.measuredHeight=M(a,ea,0);else{var za=a.style.measure(ba,l,ra,O);a.layout.measuredWidth=M(a,ca,l===ta||l===va?za.width+T:d-W),a.layout.measuredHeight=M(a,ea,O===ta||O===va?za.height+V:e-_)}}else{var Ba=a.children.length;if(0===Ba)return a.layout.measuredWidth=M(a,ca,l===ta||l===va?T:d-W),void(a.layout.measuredHeight=M(a,ea,O===ta||O===va?V:e-_));if(!R){if(l===va&&0>=d&&O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,0));if(l===va&&0>=d)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,b(e)?0:e-_));if(O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,b(d)?0:d-W),void(a.layout.measuredHeight=M(a,ea,0));if(l===ua&&O===ua)return a.layout.measuredWidth=M(a,ca,d-W),void(a.layout.measuredHeight=M(a,ea,e-_))}var Ca,Da,Ea,Fa,Ga,Ha,Ia=v(x(a),aa),Ja=y(Ia,aa),Ka=c(Ia),La=s(a),Ma=C(a),Na=void 0,Oa=void 0,Pa=o(a,Ia),Qa=p(a,Ia),Ra=o(a,Ja),Sa=r(a,Ia),Ta=r(a,Ja),Ua=Ka?l:O,Va=Ka?O:l,Wa=d-W-T,Xa=e-_-V,Ya=Ka?Wa:Xa,Za=Ka?Xa:Wa;for(Da=0;Ba>Da;Da++){if(Ca=a.children[Da],R){var $a=w(Ca,aa);P(Ca,$a)}z(Ca)===qa?(void 0===Na&&(Na=Ca),void 0!==Oa&&(Oa.nextChild=Ca),Oa=Ca,Ca.nextChild=void 0):Ka&&E(Ca,ca)?Ca.layout.flexBasis=L(Ca.style.width,r(Ca,ca)):!Ka&&E(Ca,ea)?Ca.layout.flexBasis=L(Ca.style.height,r(Ca,ea)):f(Ca)||b(Ya)?(Ea=U,Fa=U,Ga=ta,Ha=ta,E(Ca,ca)&&(Ea=Ca.style.width+q(Ca,ca),Ga=ua),E(Ca,ea)&&(Fa=Ca.style.height+q(Ca,ea),Ha=ua),Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Ca,Ea,Fa,aa,Ga,Ha,!1,"measure"),Ca.layout.flexBasis=L(Ka?Ca.layout.measuredWidth:Ca.layout.measuredHeight,r(Ca,Ia))):Ca.layout.flexBasis=L(0,r(Ca,Ia))}for(var _a=0,ab=0,bb=0,cb=0,db=0;Ba>ab;){var eb=0,fb=0,gb=0,hb=0;Da=_a;for(var ib=void 0,jb=void 0;Ba>Da;){if(Ca=a.children[Da],Ca.lineIndex=bb,z(Ca)!==qa){var kb=Ca.layout.flexBasis+q(Ca,Ia);if(fb+kb>Ya&&Ma&&eb>0)break;fb+=kb,eb++,B(Ca)&&(gb+=g(Ca),hb+=h(Ca)*Ca.layout.flexBasis),void 0===ib&&(ib=Ca),void 0!==jb&&(jb.nextChild=Ca),jb=Ca,Ca.nextChild=void 0}Da++,ab++}var lb=!R&&Va===ua,mb=0,nb=0,ob=0;b(Ya)?0>fb&&(ob=-fb):ob=Ya-fb;var pb=ob,qb=0;if(!lb){var rb,sb,tb,ub,vb,wb=0,xb=0;for(jb=ib;void 0!==jb;)rb=jb.layout.flexBasis,0>ob?(sb=h(jb)*rb,0!==sb&&(ub=rb+ob/hb*sb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,wb-=sb))):ob>0&&(tb=g(jb),0!==tb&&(ub=rb+ob/gb*tb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,xb-=tb))),jb=jb.nextChild;for(hb+=wb,gb+=xb,ob+=qb,qb=0,jb=ib;void 0!==jb;){rb=jb.layout.flexBasis;var yb=rb;0>ob?(sb=h(jb)*rb,0!==sb&&(yb=M(jb,Ia,rb+ob/hb*sb))):ob>0&&(tb=g(jb),0!==tb&&(yb=M(jb,Ia,rb+ob/gb*tb))),qb-=yb-rb,Ka?(Ea=yb+q(jb,ca),Ga=ua,E(jb,ea)?(Fa=jb.style.height+q(jb,ea),Ha=ua):(Fa=Za,Ha=b(Fa)?ta:va)):(Fa=yb+q(jb,ea),Ha=ua,E(jb,ca)?(Ea=jb.style.width+q(jb,ca),Ga=ua):(Ea=Za,Ga=b(Ea)?ta:va));var zb=!E(jb,Ja)&&u(a,jb)===oa;S(jb,Ea,Fa,aa,Ga,Ha,R&&!zb,"flex"),jb=jb.nextChild}}ob=pb+qb,Ua===va&&(ob=0),La!==ga&&(La===ha?mb=ob/2:La===ia?mb=ob:La===ja?(ob=L(ob,0),nb=eb>1?ob/(eb-1):0):La===ka&&(nb=ob/eb,mb=nb/2));var Ab=Pa+mb,Bb=0;for(Da=_a;ab>Da;++Da)Ca=a.children[Da],z(Ca)===qa&&G(Ca,wa[Ia])?R&&(Ca.layout[ya[Ia]]=I(Ca,wa[Ia])+m(a,Ia)+i(Ca,Ia)):(R&&(Ca.layout[ya[Ia]]+=Ab),z(Ca)===pa&&(lb?(Ab+=nb+q(Ca,Ia)+Ca.layout.flexBasis,Bb=Za):(Ab+=nb+D(Ca,Ia),Bb=L(Bb,D(Ca,Ja)))));Ab+=Qa;var Cb=Za;if(Va!==ta&&Va!==va||(Cb=M(a,Ja,Bb+Ta)-Ta,Va===va&&(Cb=K(Cb,Za))),Ma||Va!==ua||(Bb=Za),Bb=M(a,Ja,Bb+Ta)-Ta,R)for(Da=_a;ab>Da;++Da)if(Ca=a.children[Da],z(Ca)===qa)G(Ca,wa[Ja])?Ca.layout[ya[Ja]]=I(Ca,wa[Ja])+m(a,Ja)+i(Ca,Ja):Ca.layout[ya[Ja]]=Ra+i(Ca,Ja);else{var Db=Ra,Eb=u(a,Ca);if(Eb===oa){Ea=Ca.layout.measuredWidth+q(Ca,ca),Fa=Ca.layout.measuredHeight+q(Ca,ea);var Fb=!1;Ka?(Fb=E(Ca,ea),Fa=Bb):(Fb=E(Ca,ca),Ea=Bb),Fb||(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,S(Ca,Ea,Fa,aa,Ga,Ha,!0,"stretch"))}else if(Eb!==la){var Gb=Cb-D(Ca,Ja);Db+=Eb===ma?Gb/2:Gb}Ca.layout[ya[Ja]]+=cb+Db}cb+=Bb,db=L(db,Ab),bb++,_a=ab,ab=_a}if(bb>1&&R&&!b(Za)){var Hb=Za-cb,Ib=0,Jb=Ra,Kb=t(a);Kb===na?Jb+=Hb:Kb===ma?Jb+=Hb/2:Kb===oa&&Za>cb&&(Ib=Hb/bb);var Lb=0;for(Da=0;bb>Da;++Da){var Mb,Nb=Lb,Ob=0;for(Mb=Nb;Ba>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){if(Ca.lineIndex!==Da)break;F(Ca,Ja)&&(Ob=L(Ob,Ca.layout[Aa[Ja]]+q(Ca,Ja)))}if(Lb=Mb,Ob+=Ib,R)for(Mb=Nb;Lb>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){var Pb=u(a,Ca);Pb===la?Ca.layout[ya[Ja]]=Jb+i(Ca,Ja):Pb===na?Ca.layout[ya[Ja]]=Jb+Ob-j(Ca,Ja)-Ca.layout[Aa[Ja]]:Pb===ma?(Fa=Ca.layout[Aa[Ja]],Ca.layout[ya[Ja]]=Jb+(Ob-Fa)/2):Pb===oa&&(Ca.layout[ya[Ja]]=Jb+i(Ca,Ja))}Jb+=Ob}}if(a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_),Ua===ta?a.layout[Aa[Ia]]=M(a,Ia,db):Ua===va&&(a.layout[Aa[Ia]]=L(K(Ya+Sa,J(a,Ia,db)),Sa)),Va===ta?a.layout[Aa[Ja]]=M(a,Ja,cb+Ta):Va===va&&(a.layout[Aa[Ja]]=L(K(Za+Ta,J(a,Ja,cb+Ta)),Ta)),R){var Qb=!1,Rb=!1;if(Ia!==da&&Ia!==fa||(Qb=!0),Ja!==da&&Ja!==fa||(Rb=!0),Qb||Rb)for(Da=0;Ba>Da;++Da)Ca=a.children[Da],Qb&&N(a,Ca,Ia),Rb&&N(a,Ca,Ja)}for(Oa=Na;void 0!==Oa;)R&&(Ea=U,Fa=U,E(Oa,ca)?Ea=Oa.style.width+q(Oa,ca):G(Oa,X)&&G(Oa,Z)&&(Ea=a.layout.measuredWidth-(m(a,ca)+n(a,ca))-(Oa.style[X]+Oa.style[Z]),Ea=M(Oa,ca,Ea)),E(Oa,ea)?Fa=Oa.style.height+q(Oa,ea):G(Oa,Y)&&G(Oa,$)&&(Fa=a.layout.measuredHeight-(m(a,ea)+n(a,ea))-(Oa.style[Y]+Oa.style[$]),Fa=M(Oa,ea,Fa)),(b(Ea)||b(Fa))&&(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Oa,Ea,Fa,aa,Ga,Ha,!1,"abs-measure"),Ea=Oa.layout.measuredWidth+q(Oa,ca),Fa=Oa.layout.measuredHeight+q(Oa,ea)),S(Oa,Ea,Fa,aa,ua,ua,!0,"abs-layout"),G(Oa,xa[ca])&&!G(Oa,wa[ca])&&(Oa.layout[wa[ca]]=a.layout[Aa[ca]]-Oa.layout[Aa[ca]]-I(Oa,xa[ca])),G(Oa,xa[ea])&&!G(Oa,wa[ea])&&(Oa.layout[wa[ea]]=a.layout[Aa[ea]]-Oa.layout[Aa[ea]]-I(Oa,xa[ea]))),Oa=Oa.nextChild}}function S(a,b,c,d,e,f,g,h){var i=a.layout,j=a.isDirty&&i.generationCount!==W||i.lastParentDirection!==d;j&&(void 0!==i.cachedMeasurements&&(i.cachedMeasurements=[]),void 0!==i.cachedLayout&&(i.cachedLayout.widthMeasureMode=void 0,i.cachedLayout.heightMeasureMode=void 0));var k;if(g)i.cachedLayout&&i.cachedLayout.availableWidth===b&&i.cachedLayout.availableHeight===c&&i.cachedLayout.widthMeasureMode===e&&i.cachedLayout.heightMeasureMode===f&&(k=i.cachedLayout);else if(i.cachedMeasurements)for(var l=0,m=i.cachedMeasurements.length;m>l;l++)if(i.cachedMeasurements[l].availableWidth===b&&i.cachedMeasurements[l].availableHeight===c&&i.cachedMeasurements[l].widthMeasureMode===e&&i.cachedMeasurements[l].heightMeasureMode===f){k=i.cachedMeasurements[l];break}if(j||void 0===k){if(R(a,b,c,d,e,f,g),i.lastParentDirection=d,void 0===k){var n;g?(void 0===i.cachedLayout&&(i.cachedLayout={}),n=i.cachedLayout):(void 0===i.cachedMeasurements&&(i.cachedMeasurements=[]),n={},i.cachedMeasurements.push(n)),n.availableWidth=b,n.availableHeight=c,n.widthMeasureMode=e,n.heightMeasureMode=f,n.computedWidth=i.measuredWidth,n.computedHeight=i.measuredHeight}}else i.measureWidth=k.computedWidth,i.measureHeight=k.computedHeight;return g&&(a.layout.width=a.layout.measuredWidth,a.layout.height=a.layout.measuredHeight,i.shouldUpdate=!0),i.generationCount=W,j||void 0===k}function T(a,c,d,e){W++,b(c)&&E(a,ca)&&(c=a.style.width+q(a,ca)),b(d)&&E(a,ea)&&(d=a.style.height+q(a,ea));var f=b(c)?ta:ua,g=b(d)?ta:ua;S(a,c,d,e,f,g,!0,"initial")&&P(a,a.layout.direction)}var U,V=!1,W=0,X="left",Y="top",Z="right",$="bottom",_="inherit",aa="ltr",ba="rtl",ca="row",da="row-reverse",ea="column",fa="column-reverse",ga="flex-start",ha="center",ia="flex-end",ja="space-between",ka="space-around",la="flex-start",ma="center",na="flex-end",oa="stretch",pa="relative",qa="absolute",ra="visible",sa="hidden",ta="undefined",ua="exactly",va="at-most",wa={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},xa={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},ya={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},za={row:"width","row-reverse":"width",column:"height","column-reverse":"height"},Aa={row:"measuredWidth","row-reverse":"measuredWidth",column:"measuredHeight","column-reverse":"measuredHeight"};return{layoutNodeImpl:R,computeLayout:T,fillNodes:a}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); //# sourceMappingURL=css-layout.min.js.map \ No newline at end of file diff --git a/dist/css-layout.min.js.map b/dist/css-layout.min.js.map index 58ab8daa..4f9fb2e9 100644 --- a/dist/css-layout.min.js.map +++ b/dist/css-layout.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","Number","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getFlex","flex","isFlexBasisAuto","POSITIVE_FLEX_IS_AUTO","getFlexGrowFactor","getFlexShrinkFactor","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","CSS_POSITION_RELATIVE","getOverflow","overflow","CSS_OVERFLOW_VISIBLE","isFlex","isFlexWrap","flexWrap","getDimWithMargin","measuredDim","isStyleDimDefined","dim","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxisWithinMinAndMax","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fminf","a","b","fmaxf","boundAxis","setTrailingPosition","size","CSS_POSITION_ABSOLUTE","trailing","getRelativePosition","leading","setPosition","mainAxis","crossAxis","assert","condition","message","layoutNodeImpl","availableWidth","availableHeight","widthMeasureMode","heightMeasureMode","performLayout","CSS_MEASURE_MODE_UNDEFINED","paddingAndBorderAxisRow","paddingAndBorderAxisColumn","marginAxisRow","marginAxisColumn","innerWidth","innerHeight","CSS_MEASURE_MODE_EXACTLY","measuredWidth","measuredHeight","measureDim","CSS_MEASURE_MODE_AT_MOST","childCount","i","childWidth","childHeight","childWidthMeasureMode","childHeightMeasureMode","isMainAxisRow","isNodeFlexWrap","firstAbsoluteChild","currentAbsoluteChild","leadingPaddingAndBorderMain","trailingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","measureModeMainDim","measureModeCrossDim","availableInnerWidth","availableInnerHeight","availableInnerMainDim","availableInnerCrossDim","childDirection","nextChild","flexBasis","CSS_UNDEFINED","CSS_OVERFLOW_HIDDEN","layoutNodeInternal","startOfLineIndex","endOfLineIndex","lineCount","totalLineCrossDim","maxLineMainDim","itemsOnLine","sizeConsumedOnCurrentLine","totalFlexGrowFactors","totalFlexShrinkScaledFactors","firstRelativeChild","currentRelativeChild","lineIndex","outerFlexBasis","canSkipFlex","leadingMainDim","betweenMainDim","remainingFreeSpace","remainingFreeSpaceAfterFlex","childFlexBasis","flexShrinkScaledFactor","flexGrowFactor","baseMainSize","boundMainSize","deltaFreeSpace","deltaFlexShrinkScaledFactors","deltaFlexGrowFactors","updatedMainSize","requiresStretchLayout","CSS_ALIGN_STRETCH","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","mainDim","crossDim","containerCrossAxis","leadingCrossDim","alignItem","isCrossSizeDefinite","CSS_ALIGN_FLEX_START","remainingCrossDim","CSS_ALIGN_CENTER","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","j","startIndex","lineHeight","alignContentAlignItem","needsMainTrailingPos","needsCrossTrailingPos","CSS_LEFT","CSS_RIGHT","CSS_TOP","CSS_BOTTOM","reason","needToVisitNode","generationCount","gCurrentGenerationCount","lastParentDirection","cachedMeasurements","cachedLayout","cachedResults","len","newCacheEntry","push","computedWidth","computedHeight","measureWidth","measureHeight","shouldUpdate","layoutNode"],"mappings":"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","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n \n var POSITIVE_FLEX_IS_AUTO = false;\n \n var gCurrentGenerationCount = 0;\n \n var CSS_UNDEFINED;\n \n var CSS_LEFT = 'left';\n var CSS_TOP = 'top';\n var CSS_RIGHT = 'right';\n var CSS_BOTTOM = 'bottom';\n \n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n \n var CSS_OVERFLOW_VISIBLE = 'visible';\n var CSS_OVERFLOW_HIDDEN = 'hidden';\n \n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n var measuredDim = {\n 'row': 'measuredWidth',\n 'row-reverse': 'measuredWidth',\n 'column': 'measuredHeight',\n 'column-reverse': 'measuredHeight'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || Number.isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n \n function getFlex(node) {\n if (node.style.flex === undefined) {\n return 0;\n }\n return node.style.flex;\n }\n \n function isFlexBasisAuto(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // All flex values are auto.\n return true;\n } else {\n // A flex value > 0 implies a basis of zero.\n return getFlex(node) <= 0;\n }\n }\n \n function getFlexGrowFactor(node) {\n // Flex grow is implied by positive values for flex.\n if (getFlex(node) > 0) {\n return getFlex(node);\n }\n return 0;\n }\n \n function getFlexShrinkFactor(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // A flex shrink factor of 1 is implied by non-zero values for flex.\n if (getFlex(node) !== 0) {\n return 1;\n }\n } else {\n // A flex shrink factor of 1 is implied by negative values for flex.\n if (getFlex(node) < 0) {\n return 1;\n }\n }\n return 0;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return CSS_POSITION_RELATIVE;\n }\n \n function getOverflow(node) {\n if (node.style.overflow) {\n return node.style.overflow;\n }\n return CSS_OVERFLOW_VISIBLE;\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex !== undefined && node.style.flex !== 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[measuredDim[axis]] + getMarginAxis(node, axis);\n }\n \n function isStyleDimDefined(node, axis) { \n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n \n function isLayoutDimDefined(node, axis) { \n return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n \n function boundAxisWithinMinAndMax(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n \n function fminf(a, b) {\n if (a < b) {\n return a;\n }\n return b;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n \n // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the\n // padding and border amount.\n function boundAxis(node, axis, value) {\n return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis));\n }\n\n function setTrailingPosition(node, child, axis) {\n var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ?\n 0 :\n child.layout[measuredDim[axis]];\n child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n \n function setPosition(node, direction) {\n var mainAxis = resolveAxis(getFlexDirection(node), direction);\n var crossAxis = getCrossFlexDirection(mainAxis, direction);\n \n node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n }\n \n function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n }\n \n //\n // This is the main routine that implements a subset of the flexbox layout algorithm\n // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.\n //\n // Limitations of this algorithm, compared to the full standard:\n // * Display property is always assumed to be 'flex' except for Text nodes, which\n // are assumed to be 'inline-flex'.\n // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are\n // stacked in document order.\n // * The 'order' property is not supported. The order of flex items is always defined\n // by document order.\n // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'\n // and 'hidden' are not supported.\n // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The\n // rarely-used 'wrap-reverse' is not supported.\n // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and\n // flexBasis, this algorithm supports only the three most common combinations:\n // flex: 0 is equiavlent to flex: 0 0 auto\n // flex: n (where n is a positive value) is equivalent to flex: n 1 auto\n // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0\n // This is faster because the content doesn't need to be measured, but it's\n // less flexible because the basis is always 0 and can't be overriden with\n // the width/height attributes.\n // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto\n // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel\n // values, and the default value is 0.\n // * The 'baseline' value is not supported for alignItems and alignSelf properties.\n // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be\n // specified as pixel values, not as percentages.\n // * There is no support for calculation of dimensions based on intrinsic aspect ratios\n // (e.g. images).\n // * There is no support for forced breaks.\n // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).\n //\n // Deviations from standard:\n // * Section 4.5 of the spec indicates that all flex items have a default minimum\n // main size. For text blocks, for example, this is the width of the widest word. \n // Calculating the minimum width is expensive, so we forego it and assume a default \n // minimum main size of 0.\n // * Min/Max sizes in the main axis are not honored when resolving flexible lengths.\n // * The spec indicates that the default value for 'flexDirection' is 'row', but\n // the algorithm below assumes a default of 'column'.\n //\n // Input parameters:\n // - node: current node to be sized and layed out\n // - availableWidth & availableHeight: available size to be used for sizing the node\n // or CSS_UNDEFINED if the size is not available; interpretation depends on layout\n // flags\n // - parentDirection: the inline (text) direction within the parent (left-to-right or\n // right-to-left)\n // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)\n // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)\n // - performLayout: specifies whether the caller is interested in just the dimensions\n // of the node or it requires the entire node and its subtree to be layed out\n // (with final positions)\n //\n // Details:\n // This routine is called recursively to lay out subtrees of flexbox elements. It uses the\n // information in node.style, which is treated as a read-only input. It is responsible for\n // setting the layout.direction and layout.measured_dimensions fields for the input node as well\n // as the layout.position and layout.line_index fields for its child nodes. The\n // layout.measured_dimensions field includes any border or padding for the node but does\n // not include margins.\n //\n // The spec describes four different layout modes: \"fill available\", \"max content\", \"min content\",\n // and \"fit content\". Of these, we don't use \"min content\" because we don't support default\n // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode\n // from the spec (https://www.w3.org/TR/css3-sizing/#terms):\n // - CSS_MEASURE_MODE_UNDEFINED: max content\n // - CSS_MEASURE_MODE_EXACTLY: fill available\n // - CSS_MEASURE_MODE_AT_MOST: fit content\n // \n // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of\n // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.\n //\n function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) {\n assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n \n var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n // Set the resolved resolution in the node's layout.\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n node.layout.direction = direction;\n\n // For content (text) nodes, determine the dimensions based on the text contents.\n if (isMeasureDefined(node)) {\n var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n \n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n\n // Don't bother sizing the text if both dimensions are already defined.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n } else if (innerWidth <= 0) {\n\n // Don't bother sizing the text if there's no horizontal space.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n } else {\n\n // Measure the text under the current constraints.\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n innerWidth,\n widthMeasureMode,\n innerHeight,\n heightMeasureMode\n );\n\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.width + paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.height + paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n }\n \n return;\n }\n\n // For nodes with no children, use the available values if they were provided, or\n // the minimum size as indicated by the padding and border sizes.\n var/*int*/ childCount = node.children.length;\n if (childCount === 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n return;\n }\n\n // If we're not being asked to perform a full layout, we can handle a number of common\n // cases here without incurring the cost of the remaining function.\n if (!performLayout) {\n // If we're being asked to size the content with an at most constraint but there is no available width,\n // the measurement will always be zero.\n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 &&\n heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn));\n return;\n }\n\n if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow));\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n // If we're being asked to use an exact width/height, there's no need to measure the children.\n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n return;\n }\n }\n\n // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*bool*/ isMainAxisRow = isRowDirection(mainAxis);\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_node_t**/ firstAbsoluteChild = undefined;\n var/*css_node_t**/ currentAbsoluteChild = undefined;\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n \n var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;\n var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;\n\n // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS\n var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;\n var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;\n\n // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM\n var/*css_node_t**/ child;\n var/*int*/ i;\n var/*float*/ childWidth;\n var/*float*/ childHeight;\n var/*css_measure_mode_t*/ childWidthMeasureMode;\n var/*css_measure_mode_t*/ childHeightMeasureMode;\n for (i = 0; i < childCount; i++) {\n child = node.children[i];\n\n if (performLayout) {\n // Set the initial position (relative to the parent).\n var/*css_direction_t*/ childDirection = resolveDirection(child, direction);\n setPosition(child, childDirection);\n }\n \n // Absolute-positioned children don't participate in flex layout. Add them\n // to a list that we can process later.\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === undefined) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== undefined) {\n currentAbsoluteChild.nextChild = child;\n }\n currentAbsoluteChild = child;\n child.nextChild = undefined;\n } else {\n \n if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n \n // The width is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW));\n } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n \n // The height is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN));\n } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) {\n \n // If the basis isn't 'auto', it is assumed to be zero.\n child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));\n } else {\n \n // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n \n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n // Measure the child\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure');\n \n child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis));\n }\n }\n }\n\n // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES\n \n // Indexes of children that represent the first and last items in the line.\n var/*int*/ startOfLineIndex = 0;\n var/*int*/ endOfLineIndex = 0;\n \n // Number of lines.\n var/*int*/ lineCount = 0;\n \n // Accumulated cross dimensions of all lines so far.\n var/*float*/ totalLineCrossDim = 0;\n\n // Max main dimension of all the lines.\n var/*float*/ maxLineMainDim = 0;\n\n while (endOfLineIndex < childCount) {\n \n // Number of items on the currently line. May be different than the difference\n // between start and end indicates because we skip over absolute-positioned items.\n var/*int*/ itemsOnLine = 0;\n\n // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin\n // of all the children on the current line. This will be used in order to\n // either set the dimensions of the node if none already exist or to compute\n // the remaining space left for the flexible children.\n var/*float*/ sizeConsumedOnCurrentLine = 0;\n\n var/*float*/ totalFlexGrowFactors = 0;\n var/*float*/ totalFlexShrinkScaledFactors = 0;\n\n i = startOfLineIndex;\n\n // Maintain a linked list of the child nodes that can shrink and/or grow.\n var/*css_node_t**/ firstRelativeChild = undefined;\n var/*css_node_t**/ currentRelativeChild = undefined;\n\n // Add items to the current line until it's full or we run out of items.\n while (i < childCount) {\n child = node.children[i];\n child.lineIndex = lineCount;\n\n if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) {\n var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis);\n \n // If this is a multi-line flow and this item pushes us over the available size, we've\n // hit the end of the current line. Break out of the loop and lay out the current line.\n if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {\n break;\n }\n\n sizeConsumedOnCurrentLine += outerFlexBasis;\n itemsOnLine++;\n\n if (isFlex(child)) {\n totalFlexGrowFactors += getFlexGrowFactor(child);\n \n // Unlike the grow factor, the shrink factor is scaled relative to the child\n // dimension.\n totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;\n }\n\n // Store a private linked list of children that need to be layed out.\n if (firstRelativeChild === undefined) {\n firstRelativeChild = child;\n }\n if (currentRelativeChild !== undefined) {\n currentRelativeChild.nextChild = child;\n }\n currentRelativeChild = child;\n child.nextChild = undefined;\n }\n \n i++;\n endOfLineIndex++;\n }\n \n // If we don't need to measure the cross axis, we can skip the entire flex step.\n var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY;\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS\n // Calculate the remaining available space that needs to be allocated.\n // If the main dimension size isn't known, it is computed based on\n // the line length, so there's no more space left to distribute.\n var/*float*/ remainingFreeSpace = 0;\n if (!isUndefined(availableInnerMainDim)) {\n remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;\n } else if (sizeConsumedOnCurrentLine < 0) {\n // availableInnerMainDim is indefinite which means the node is being sized based on its content.\n // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for\n // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.\n remainingFreeSpace = -sizeConsumedOnCurrentLine;\n }\n \n var/*float*/ remainingFreeSpaceAfterFlex = remainingFreeSpace;\n\n if (!canSkipFlex) {\n var/*float*/ childFlexBasis;\n var/*float*/ flexShrinkScaledFactor;\n var/*float*/ flexGrowFactor;\n var/*float*/ baseMainSize;\n var/*float*/ boundMainSize;\n \n // Do two passes over the flex items to figure out how to distribute the remaining space.\n // The first pass finds the items whose min/max constraints trigger, freezes them at those\n // sizes, and excludes those sizes from the remaining space. The second pass sets the size\n // of each flexible item. It distributes the remaining space amongst the items whose min/max\n // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing\n // their min/max constraints to trigger again. \n //\n // This two pass approach for resolving min/max constraints deviates from the spec. The\n // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process\n // that needs to be repeated a variable number of times. The algorithm implemented here\n // won't handle all cases but it was simpler to implement and it mitigates performance\n // concerns because we know exactly how many passes it'll do.\n \n // First pass: detect the flex items whose min/max constraints trigger\n var/*float*/ deltaFreeSpace = 0;\n var/*float*/ deltaFlexShrinkScaledFactors = 0;\n var/*float*/ deltaFlexGrowFactors = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize;\n deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;\n }\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize;\n deltaFlexGrowFactors -= flexGrowFactor;\n }\n }\n }\n \n currentRelativeChild = currentRelativeChild.nextChild;\n }\n \n totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;\n totalFlexGrowFactors += deltaFlexGrowFactors;\n remainingFreeSpace += deltaFreeSpace;\n remainingFreeSpaceAfterFlex = remainingFreeSpace;\n \n // Second pass: resolve the sizes of the flexible items\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n var/*float*/ updatedMainSize = childFlexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);\n }\n }\n \n remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis;\n \n if (isMainAxisRow) {\n childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = availableInnerCrossDim;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n } else {\n childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = availableInnerCrossDim;\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n }\n \n var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) &&\n getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH;\n\n // Recursively call the layout algorithm for this child with the updated main size.\n layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex');\n\n currentRelativeChild = currentRelativeChild.nextChild;\n }\n }\n \n remainingFreeSpace = remainingFreeSpaceAfterFlex;\n\n // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION\n\n // At this point, all the children have their dimensions set in the main axis.\n // Their dimensions are also set in the cross axis with the exception of items\n // that are aligned 'stretch'. We need to compute these stretch values and\n // set the final positions.\n\n // If we are using \"at most\" rules in the main axis, we won't distribute\n // any remaining space at this point.\n if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n remainingFreeSpace = 0;\n }\n\n // Use justifyContent to figure out how to allocate the remaining space\n // available in the main axis.\n if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingFreeSpace / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingFreeSpace;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingFreeSpace = fmaxf(remainingFreeSpace, 0);\n if (itemsOnLine > 1) {\n betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingFreeSpace / itemsOnLine;\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim;\n var/*float*/ crossDim = 0;\n\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n if (performLayout) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n }\n } else {\n if (performLayout) {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n }\n \n // Now that we placed the element, we need to update the variables.\n // We need to do that only for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n if (canSkipFlex) {\n // If we skipped the flex step, then we can't rely on the measuredDims because\n // they weren't computed. This means we can't call getDimWithMargin.\n mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis;\n crossDim = availableInnerCrossDim;\n } else {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n \n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n }\n }\n }\n }\n\n mainDim += trailingPaddingAndBorderMain;\n \n var/*float*/ containerCrossAxis = availableInnerCrossDim;\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n // Compute the cross axis from the max cross dimension of the children.\n containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n \n if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim);\n }\n }\n\n // If there's no flex wrap, the cross dimension is defined by the container.\n if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) {\n crossDim = availableInnerCrossDim;\n }\n\n // Clamp to the min/max size specified on the container.\n crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n\n // STEP 7: CROSS-AXIS ALIGNMENT\n // We can skip child alignment if we're just measuring the container.\n if (performLayout) {\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // If the child is absolutely positioned and has a top/left/bottom/right\n // set, override all the previously computed positions to set it correctly.\n if (isPosDefined(child, leading[crossAxis])) {\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n } else {\n child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross +\n getLeadingMargin(child, crossAxis);\n }\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n \n // If the child uses align stretch, we need to lay it out one more time, this time\n // forcing the cross-axis size to be the computed cross size for the current line.\n if (alignItem === CSS_ALIGN_STRETCH) {\n childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n var/*bool*/ isCrossSizeDefinite = false;\n \n if (isMainAxisRow) {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeight = crossDim;\n } else {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW);\n childWidth = crossDim;\n }\n \n // If the child defines a definite size for its cross axis, there's no need to stretch.\n if (!isCrossSizeDefinite) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch');\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;\n }\n }\n }\n\n totalLineCrossDim += crossDim;\n maxLineMainDim = fmaxf(maxLineMainDim, mainDim);\n\n // Reset variables for new line.\n lineCount++;\n startOfLineIndex = endOfLineIndex;\n endOfLineIndex = startOfLineIndex;\n }\n\n // STEP 8: MULTI-LINE CONTENT ALIGNMENT\n if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) {\n var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (availableInnerCrossDim > totalLineCrossDim) {\n crossDimLead = (remainingAlignContentDim / lineCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < lineCount; ++i) {\n var/*int*/ startIndex = endIndex;\n var/*int*/ j;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (j = startIndex; j < childCount; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(lineHeight,\n child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis));\n }\n }\n endIndex = j;\n lineHeight += crossDimLead;\n\n if (performLayout) {\n for (j = startIndex; j < endIndex; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n childHeight = child.layout[measuredDim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with indefinite\n // (auto) crossAxis dimension.\n }\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n // STEP 9: COMPUTING FINAL DIMENSIONS\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n\n // If the user didn't specify a width or height for the node, set the\n // dimensions based on the children.\n if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);\n } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[mainAxis]] = fmaxf(\n fminf(availableInnerMainDim + paddingAndBorderAxisMain,\n boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),\n paddingAndBorderAxisMain);\n }\n\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);\n } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[crossAxis]] = fmaxf(\n fminf(availableInnerCrossDim + paddingAndBorderAxisCross,\n boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),\n paddingAndBorderAxisCross);\n }\n \n // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN\n if (performLayout) {\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n\n // Set trailing position if necessary.\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n }\n \n // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== undefined) {\n // Now that we know the bounds of the container, perform layout again on the\n // absolutely-positioned children.\n if (performLayout) {\n\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n\n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n } else {\n // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) {\n childWidth = node.layout.measuredWidth -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) -\n (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]);\n childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);\n }\n }\n \n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n } else {\n // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) {\n childHeight = node.layout.measuredHeight -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) -\n (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]);\n childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);\n }\n }\n\n // If we're still missing one or the other dimension, measure the content.\n if (isUndefined(childWidth) || isUndefined(childHeight)) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure');\n childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout');\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]);\n }\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]);\n }\n }\n\n currentAbsoluteChild = currentAbsoluteChild.nextChild;\n }\n }\n \n //\n // This is a wrapper around the layoutNodeImpl function. It determines\n // whether the layout request is redundant and can be skipped.\n //\n // Parameters:\n // Input parameters are the same as layoutNodeImpl (see above)\n // Return parameter is true if layout was performed, false if skipped\n //\n function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection,\n widthMeasureMode, heightMeasureMode, performLayout, reason) {\n var layout = node.layout;\n\n var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) ||\n layout.lastParentDirection !== parentDirection;\n\n if (needToVisitNode) {\n // Invalidate the cached results.\n if (layout.cachedMeasurements !== undefined) {\n layout.cachedMeasurements = []; \n }\n if (layout.cachedLayout !== undefined) {\n layout.cachedLayout.widthMeasureMode = undefined;\n layout.cachedLayout.heightMeasureMode = undefined;\n }\n }\n\n var cachedResults;\n \n // Determine whether the results are already cached. We maintain a separate\n // cache for layouts and measurements. A layout operation modifies the positions\n // and dimensions for nodes in the subtree. The algorithm assumes that each node\n // gets layed out a maximum of one time per tree layout, but multiple measurements\n // may be required to resolve all of the flex dimensions.\n if (performLayout) {\n if (layout.cachedLayout &&\n layout.cachedLayout.availableWidth === availableWidth &&\n layout.cachedLayout.availableHeight === availableHeight &&\n layout.cachedLayout.widthMeasureMode === widthMeasureMode &&\n layout.cachedLayout.heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedLayout;\n }\n } else if (layout.cachedMeasurements) {\n for (var i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (layout.cachedMeasurements[i].availableWidth === availableWidth &&\n layout.cachedMeasurements[i].availableHeight === availableHeight &&\n layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode &&\n layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n \n if (!needToVisitNode && cachedResults !== undefined) {\n layout.measureWidth = cachedResults.computedWidth;\n layout.measureHeight = cachedResults.computedHeight;\n } else {\n layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);\n layout.lastParentDirection = parentDirection;\n \n if (cachedResults === undefined) {\n var newCacheEntry;\n if (performLayout) {\n // Use the single layout cache entry.\n if (layout.cachedLayout === undefined) {\n layout.cachedLayout = {};\n }\n newCacheEntry = layout.cachedLayout;\n } else {\n // Allocate a new measurement cache entry.\n if (layout.cachedMeasurements === undefined) {\n layout.cachedMeasurements = [];\n }\n newCacheEntry = {};\n layout.cachedMeasurements.push(newCacheEntry);\n }\n \n newCacheEntry.availableWidth = availableWidth;\n newCacheEntry.availableHeight = availableHeight;\n newCacheEntry.widthMeasureMode = widthMeasureMode;\n newCacheEntry.heightMeasureMode = heightMeasureMode;\n newCacheEntry.computedWidth = layout.measuredWidth;\n newCacheEntry.computedHeight = layout.measuredHeight;\n }\n }\n \n if (performLayout) {\n node.layout.width = node.layout.measuredWidth;\n node.layout.height = node.layout.measuredHeight;\n layout.shouldUpdate = true;\n }\n \n layout.generationCount = gCurrentGenerationCount;\n return (needToVisitNode || cachedResults === undefined);\n }\n \n function layoutNode(node, availableWidth, availableHeight, parentDirection) {\n // Increment the generation count. This will force the recursive routine to visit\n // all dirty nodes at least once. Subsequent visits will be skipped if the input\n // parameters don't change.\n gCurrentGenerationCount++;\n \n // If the caller didn't specify a height/width, use the dimensions\n // specified in the style.\n if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n }\n if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) {\n setPosition(node, node.layout.direction);\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file +{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","Number","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getFlex","flex","isFlexBasisAuto","POSITIVE_FLEX_IS_AUTO","getFlexGrowFactor","getFlexShrinkFactor","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","CSS_POSITION_RELATIVE","getOverflow","overflow","CSS_OVERFLOW_VISIBLE","isFlex","isFlexWrap","flexWrap","getDimWithMargin","measuredDim","isStyleDimDefined","dim","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxisWithinMinAndMax","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fminf","a","b","fmaxf","boundAxis","setTrailingPosition","size","CSS_POSITION_ABSOLUTE","trailing","getRelativePosition","leading","setPosition","mainAxis","crossAxis","assert","condition","message","layoutNodeImpl","availableWidth","availableHeight","widthMeasureMode","heightMeasureMode","performLayout","CSS_MEASURE_MODE_UNDEFINED","paddingAndBorderAxisRow","paddingAndBorderAxisColumn","marginAxisRow","marginAxisColumn","innerWidth","innerHeight","CSS_MEASURE_MODE_EXACTLY","measuredWidth","measuredHeight","measureDim","CSS_MEASURE_MODE_AT_MOST","childCount","i","childWidth","childHeight","childWidthMeasureMode","childHeightMeasureMode","isMainAxisRow","isNodeFlexWrap","firstAbsoluteChild","currentAbsoluteChild","leadingPaddingAndBorderMain","trailingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","measureModeMainDim","measureModeCrossDim","availableInnerWidth","availableInnerHeight","availableInnerMainDim","availableInnerCrossDim","childDirection","nextChild","flexBasis","CSS_UNDEFINED","CSS_OVERFLOW_HIDDEN","layoutNodeInternal","startOfLineIndex","endOfLineIndex","lineCount","totalLineCrossDim","maxLineMainDim","itemsOnLine","sizeConsumedOnCurrentLine","totalFlexGrowFactors","totalFlexShrinkScaledFactors","firstRelativeChild","currentRelativeChild","lineIndex","outerFlexBasis","canSkipFlex","leadingMainDim","betweenMainDim","remainingFreeSpace","originalRemainingFreeSpace","deltaFreeSpace","childFlexBasis","flexShrinkScaledFactor","flexGrowFactor","baseMainSize","boundMainSize","deltaFlexShrinkScaledFactors","deltaFlexGrowFactors","updatedMainSize","requiresStretchLayout","CSS_ALIGN_STRETCH","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","mainDim","crossDim","containerCrossAxis","leadingCrossDim","alignItem","isCrossSizeDefinite","CSS_ALIGN_FLEX_START","remainingCrossDim","CSS_ALIGN_CENTER","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","j","startIndex","lineHeight","alignContentAlignItem","needsMainTrailingPos","needsCrossTrailingPos","CSS_LEFT","CSS_RIGHT","CSS_TOP","CSS_BOTTOM","reason","needToVisitNode","generationCount","gCurrentGenerationCount","lastParentDirection","cachedMeasurements","cachedLayout","cachedResults","len","newCacheEntry","push","computedWidth","computedHeight","measureWidth","measureHeight","shouldUpdate","layoutNode"],"mappings":"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","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n \n var POSITIVE_FLEX_IS_AUTO = false;\n \n var gCurrentGenerationCount = 0;\n \n var CSS_UNDEFINED;\n \n var CSS_LEFT = 'left';\n var CSS_TOP = 'top';\n var CSS_RIGHT = 'right';\n var CSS_BOTTOM = 'bottom';\n \n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n \n var CSS_OVERFLOW_VISIBLE = 'visible';\n var CSS_OVERFLOW_HIDDEN = 'hidden';\n \n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n var measuredDim = {\n 'row': 'measuredWidth',\n 'row-reverse': 'measuredWidth',\n 'column': 'measuredHeight',\n 'column-reverse': 'measuredHeight'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || Number.isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n \n function getFlex(node) {\n if (node.style.flex === undefined) {\n return 0;\n }\n return node.style.flex;\n }\n \n function isFlexBasisAuto(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // All flex values are auto.\n return true;\n } else {\n // A flex value > 0 implies a basis of zero.\n return getFlex(node) <= 0;\n }\n }\n \n function getFlexGrowFactor(node) {\n // Flex grow is implied by positive values for flex.\n if (getFlex(node) > 0) {\n return getFlex(node);\n }\n return 0;\n }\n \n function getFlexShrinkFactor(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // A flex shrink factor of 1 is implied by non-zero values for flex.\n if (getFlex(node) !== 0) {\n return 1;\n }\n } else {\n // A flex shrink factor of 1 is implied by negative values for flex.\n if (getFlex(node) < 0) {\n return 1;\n }\n }\n return 0;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return CSS_POSITION_RELATIVE;\n }\n \n function getOverflow(node) {\n if (node.style.overflow) {\n return node.style.overflow;\n }\n return CSS_OVERFLOW_VISIBLE;\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex !== undefined && node.style.flex !== 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[measuredDim[axis]] + getMarginAxis(node, axis);\n }\n \n function isStyleDimDefined(node, axis) { \n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n \n function isLayoutDimDefined(node, axis) { \n return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n \n function boundAxisWithinMinAndMax(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n \n function fminf(a, b) {\n if (a < b) {\n return a;\n }\n return b;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n \n // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the\n // padding and border amount.\n function boundAxis(node, axis, value) {\n return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis));\n }\n\n function setTrailingPosition(node, child, axis) {\n var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ?\n 0 :\n child.layout[measuredDim[axis]];\n child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n \n function setPosition(node, direction) {\n var mainAxis = resolveAxis(getFlexDirection(node), direction);\n var crossAxis = getCrossFlexDirection(mainAxis, direction);\n \n node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n }\n \n function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n }\n \n //\n // This is the main routine that implements a subset of the flexbox layout algorithm\n // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.\n //\n // Limitations of this algorithm, compared to the full standard:\n // * Display property is always assumed to be 'flex' except for Text nodes, which\n // are assumed to be 'inline-flex'.\n // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are\n // stacked in document order.\n // * The 'order' property is not supported. The order of flex items is always defined\n // by document order.\n // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'\n // and 'hidden' are not supported.\n // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The\n // rarely-used 'wrap-reverse' is not supported.\n // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and\n // flexBasis, this algorithm supports only the three most common combinations:\n // flex: 0 is equiavlent to flex: 0 0 auto\n // flex: n (where n is a positive value) is equivalent to flex: n 1 auto\n // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0\n // This is faster because the content doesn't need to be measured, but it's\n // less flexible because the basis is always 0 and can't be overriden with\n // the width/height attributes.\n // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto\n // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel\n // values, and the default value is 0.\n // * The 'baseline' value is not supported for alignItems and alignSelf properties.\n // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be\n // specified as pixel values, not as percentages.\n // * There is no support for calculation of dimensions based on intrinsic aspect ratios\n // (e.g. images).\n // * There is no support for forced breaks.\n // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).\n //\n // Deviations from standard:\n // * Section 4.5 of the spec indicates that all flex items have a default minimum\n // main size. For text blocks, for example, this is the width of the widest word. \n // Calculating the minimum width is expensive, so we forego it and assume a default \n // minimum main size of 0.\n // * Min/Max sizes in the main axis are not honored when resolving flexible lengths.\n // * The spec indicates that the default value for 'flexDirection' is 'row', but\n // the algorithm below assumes a default of 'column'.\n //\n // Input parameters:\n // - node: current node to be sized and layed out\n // - availableWidth & availableHeight: available size to be used for sizing the node\n // or CSS_UNDEFINED if the size is not available; interpretation depends on layout\n // flags\n // - parentDirection: the inline (text) direction within the parent (left-to-right or\n // right-to-left)\n // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)\n // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)\n // - performLayout: specifies whether the caller is interested in just the dimensions\n // of the node or it requires the entire node and its subtree to be layed out\n // (with final positions)\n //\n // Details:\n // This routine is called recursively to lay out subtrees of flexbox elements. It uses the\n // information in node.style, which is treated as a read-only input. It is responsible for\n // setting the layout.direction and layout.measured_dimensions fields for the input node as well\n // as the layout.position and layout.line_index fields for its child nodes. The\n // layout.measured_dimensions field includes any border or padding for the node but does\n // not include margins.\n //\n // The spec describes four different layout modes: \"fill available\", \"max content\", \"min content\",\n // and \"fit content\". Of these, we don't use \"min content\" because we don't support default\n // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode\n // from the spec (https://www.w3.org/TR/css3-sizing/#terms):\n // - CSS_MEASURE_MODE_UNDEFINED: max content\n // - CSS_MEASURE_MODE_EXACTLY: fill available\n // - CSS_MEASURE_MODE_AT_MOST: fit content\n // \n // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of\n // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.\n //\n function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) {\n assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n \n var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n // Set the resolved resolution in the node's layout.\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n node.layout.direction = direction;\n\n // For content (text) nodes, determine the dimensions based on the text contents.\n if (isMeasureDefined(node)) {\n var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n \n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n\n // Don't bother sizing the text if both dimensions are already defined.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n } else if (innerWidth <= 0) {\n\n // Don't bother sizing the text if there's no horizontal space.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n } else {\n\n // Measure the text under the current constraints.\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n innerWidth,\n widthMeasureMode,\n innerHeight,\n heightMeasureMode\n );\n\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.width + paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.height + paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n }\n \n return;\n }\n\n // For nodes with no children, use the available values if they were provided, or\n // the minimum size as indicated by the padding and border sizes.\n var/*int*/ childCount = node.children.length;\n if (childCount === 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n return;\n }\n\n // If we're not being asked to perform a full layout, we can handle a number of common\n // cases here without incurring the cost of the remaining function.\n if (!performLayout) {\n // If we're being asked to size the content with an at most constraint but there is no available width,\n // the measurement will always be zero.\n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 &&\n heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn));\n return;\n }\n\n if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow));\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n // If we're being asked to use an exact width/height, there's no need to measure the children.\n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n return;\n }\n }\n\n // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*bool*/ isMainAxisRow = isRowDirection(mainAxis);\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_node_t**/ firstAbsoluteChild = undefined;\n var/*css_node_t**/ currentAbsoluteChild = undefined;\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n \n var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;\n var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;\n\n // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS\n var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;\n var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;\n\n // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM\n var/*css_node_t**/ child;\n var/*int*/ i;\n var/*float*/ childWidth;\n var/*float*/ childHeight;\n var/*css_measure_mode_t*/ childWidthMeasureMode;\n var/*css_measure_mode_t*/ childHeightMeasureMode;\n for (i = 0; i < childCount; i++) {\n child = node.children[i];\n\n if (performLayout) {\n // Set the initial position (relative to the parent).\n var/*css_direction_t*/ childDirection = resolveDirection(child, direction);\n setPosition(child, childDirection);\n }\n \n // Absolute-positioned children don't participate in flex layout. Add them\n // to a list that we can process later.\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === undefined) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== undefined) {\n currentAbsoluteChild.nextChild = child;\n }\n currentAbsoluteChild = child;\n child.nextChild = undefined;\n } else {\n \n if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n \n // The width is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW));\n } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n \n // The height is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN));\n } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) {\n \n // If the basis isn't 'auto', it is assumed to be zero.\n child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));\n } else {\n \n // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n \n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n // Measure the child\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure');\n \n child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis));\n }\n }\n }\n\n // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES\n \n // Indexes of children that represent the first and last items in the line.\n var/*int*/ startOfLineIndex = 0;\n var/*int*/ endOfLineIndex = 0;\n \n // Number of lines.\n var/*int*/ lineCount = 0;\n \n // Accumulated cross dimensions of all lines so far.\n var/*float*/ totalLineCrossDim = 0;\n\n // Max main dimension of all the lines.\n var/*float*/ maxLineMainDim = 0;\n\n while (endOfLineIndex < childCount) {\n \n // Number of items on the currently line. May be different than the difference\n // between start and end indicates because we skip over absolute-positioned items.\n var/*int*/ itemsOnLine = 0;\n\n // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin\n // of all the children on the current line. This will be used in order to\n // either set the dimensions of the node if none already exist or to compute\n // the remaining space left for the flexible children.\n var/*float*/ sizeConsumedOnCurrentLine = 0;\n\n var/*float*/ totalFlexGrowFactors = 0;\n var/*float*/ totalFlexShrinkScaledFactors = 0;\n\n i = startOfLineIndex;\n\n // Maintain a linked list of the child nodes that can shrink and/or grow.\n var/*css_node_t**/ firstRelativeChild = undefined;\n var/*css_node_t**/ currentRelativeChild = undefined;\n\n // Add items to the current line until it's full or we run out of items.\n while (i < childCount) {\n child = node.children[i];\n child.lineIndex = lineCount;\n\n if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) {\n var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis);\n \n // If this is a multi-line flow and this item pushes us over the available size, we've\n // hit the end of the current line. Break out of the loop and lay out the current line.\n if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {\n break;\n }\n\n sizeConsumedOnCurrentLine += outerFlexBasis;\n itemsOnLine++;\n\n if (isFlex(child)) {\n totalFlexGrowFactors += getFlexGrowFactor(child);\n \n // Unlike the grow factor, the shrink factor is scaled relative to the child\n // dimension.\n totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;\n }\n\n // Store a private linked list of children that need to be layed out.\n if (firstRelativeChild === undefined) {\n firstRelativeChild = child;\n }\n if (currentRelativeChild !== undefined) {\n currentRelativeChild.nextChild = child;\n }\n currentRelativeChild = child;\n child.nextChild = undefined;\n }\n \n i++;\n endOfLineIndex++;\n }\n \n // If we don't need to measure the cross axis, we can skip the entire flex step.\n var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY;\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS\n // Calculate the remaining available space that needs to be allocated.\n // If the main dimension size isn't known, it is computed based on\n // the line length, so there's no more space left to distribute.\n var/*float*/ remainingFreeSpace = 0;\n if (!isUndefined(availableInnerMainDim)) {\n remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;\n } else if (sizeConsumedOnCurrentLine < 0) {\n // availableInnerMainDim is indefinite which means the node is being sized based on its content.\n // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for\n // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.\n remainingFreeSpace = -sizeConsumedOnCurrentLine;\n }\n \n var/*float*/ originalRemainingFreeSpace = remainingFreeSpace;\n var/*float*/ deltaFreeSpace = 0;\n\n if (!canSkipFlex) {\n var/*float*/ childFlexBasis;\n var/*float*/ flexShrinkScaledFactor;\n var/*float*/ flexGrowFactor;\n var/*float*/ baseMainSize;\n var/*float*/ boundMainSize;\n \n // Do two passes over the flex items to figure out how to distribute the remaining space.\n // The first pass finds the items whose min/max constraints trigger, freezes them at those\n // sizes, and excludes those sizes from the remaining space. The second pass sets the size\n // of each flexible item. It distributes the remaining space amongst the items whose min/max\n // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing\n // their min/max constraints to trigger again. \n //\n // This two pass approach for resolving min/max constraints deviates from the spec. The\n // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process\n // that needs to be repeated a variable number of times. The algorithm implemented here\n // won't handle all cases but it was simpler to implement and it mitigates performance\n // concerns because we know exactly how many passes it'll do.\n \n // First pass: detect the flex items whose min/max constraints trigger\n var/*float*/ deltaFlexShrinkScaledFactors = 0;\n var/*float*/ deltaFlexGrowFactors = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;\n }\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexGrowFactors -= flexGrowFactor;\n }\n }\n }\n \n currentRelativeChild = currentRelativeChild.nextChild;\n }\n \n totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;\n totalFlexGrowFactors += deltaFlexGrowFactors;\n remainingFreeSpace += deltaFreeSpace;\n \n // Second pass: resolve the sizes of the flexible items\n deltaFreeSpace = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n var/*float*/ updatedMainSize = childFlexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);\n }\n }\n \n deltaFreeSpace -= updatedMainSize - childFlexBasis;\n \n if (isMainAxisRow) {\n childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = availableInnerCrossDim;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n } else {\n childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = availableInnerCrossDim;\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n }\n \n var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) &&\n getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH;\n\n // Recursively call the layout algorithm for this child with the updated main size.\n layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex');\n\n currentRelativeChild = currentRelativeChild.nextChild;\n }\n }\n \n remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace;\n\n // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION\n\n // At this point, all the children have their dimensions set in the main axis.\n // Their dimensions are also set in the cross axis with the exception of items\n // that are aligned 'stretch'. We need to compute these stretch values and\n // set the final positions.\n\n // If we are using \"at most\" rules in the main axis, we won't distribute\n // any remaining space at this point.\n if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n remainingFreeSpace = 0;\n }\n\n // Use justifyContent to figure out how to allocate the remaining space\n // available in the main axis.\n if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingFreeSpace / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingFreeSpace;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingFreeSpace = fmaxf(remainingFreeSpace, 0);\n if (itemsOnLine > 1) {\n betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingFreeSpace / itemsOnLine;\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim;\n var/*float*/ crossDim = 0;\n\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n if (performLayout) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n }\n } else {\n if (performLayout) {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n }\n \n // Now that we placed the element, we need to update the variables.\n // We need to do that only for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n if (canSkipFlex) {\n // If we skipped the flex step, then we can't rely on the measuredDims because\n // they weren't computed. This means we can't call getDimWithMargin.\n mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis;\n crossDim = availableInnerCrossDim;\n } else {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n \n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n }\n }\n }\n }\n\n mainDim += trailingPaddingAndBorderMain;\n \n var/*float*/ containerCrossAxis = availableInnerCrossDim;\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n // Compute the cross axis from the max cross dimension of the children.\n containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n \n if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim);\n }\n }\n\n // If there's no flex wrap, the cross dimension is defined by the container.\n if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) {\n crossDim = availableInnerCrossDim;\n }\n\n // Clamp to the min/max size specified on the container.\n crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n\n // STEP 7: CROSS-AXIS ALIGNMENT\n // We can skip child alignment if we're just measuring the container.\n if (performLayout) {\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // If the child is absolutely positioned and has a top/left/bottom/right\n // set, override all the previously computed positions to set it correctly.\n if (isPosDefined(child, leading[crossAxis])) {\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n } else {\n child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross +\n getLeadingMargin(child, crossAxis);\n }\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n \n // If the child uses align stretch, we need to lay it out one more time, this time\n // forcing the cross-axis size to be the computed cross size for the current line.\n if (alignItem === CSS_ALIGN_STRETCH) {\n childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n var/*bool*/ isCrossSizeDefinite = false;\n \n if (isMainAxisRow) {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeight = crossDim;\n } else {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW);\n childWidth = crossDim;\n }\n \n // If the child defines a definite size for its cross axis, there's no need to stretch.\n if (!isCrossSizeDefinite) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch');\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;\n }\n }\n }\n\n totalLineCrossDim += crossDim;\n maxLineMainDim = fmaxf(maxLineMainDim, mainDim);\n\n // Reset variables for new line.\n lineCount++;\n startOfLineIndex = endOfLineIndex;\n endOfLineIndex = startOfLineIndex;\n }\n\n // STEP 8: MULTI-LINE CONTENT ALIGNMENT\n if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) {\n var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (availableInnerCrossDim > totalLineCrossDim) {\n crossDimLead = (remainingAlignContentDim / lineCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < lineCount; ++i) {\n var/*int*/ startIndex = endIndex;\n var/*int*/ j;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (j = startIndex; j < childCount; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(lineHeight,\n child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis));\n }\n }\n endIndex = j;\n lineHeight += crossDimLead;\n\n if (performLayout) {\n for (j = startIndex; j < endIndex; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n childHeight = child.layout[measuredDim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with indefinite\n // (auto) crossAxis dimension.\n }\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n // STEP 9: COMPUTING FINAL DIMENSIONS\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n\n // If the user didn't specify a width or height for the node, set the\n // dimensions based on the children.\n if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);\n } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[mainAxis]] = fmaxf(\n fminf(availableInnerMainDim + paddingAndBorderAxisMain,\n boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),\n paddingAndBorderAxisMain);\n }\n\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);\n } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[crossAxis]] = fmaxf(\n fminf(availableInnerCrossDim + paddingAndBorderAxisCross,\n boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),\n paddingAndBorderAxisCross);\n }\n \n // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN\n if (performLayout) {\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n\n // Set trailing position if necessary.\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n }\n \n // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== undefined) {\n // Now that we know the bounds of the container, perform layout again on the\n // absolutely-positioned children.\n if (performLayout) {\n\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n\n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n } else {\n // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) {\n childWidth = node.layout.measuredWidth -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) -\n (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]);\n childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);\n }\n }\n \n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n } else {\n // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) {\n childHeight = node.layout.measuredHeight -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) -\n (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]);\n childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);\n }\n }\n\n // If we're still missing one or the other dimension, measure the content.\n if (isUndefined(childWidth) || isUndefined(childHeight)) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure');\n childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout');\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]);\n }\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]);\n }\n }\n\n currentAbsoluteChild = currentAbsoluteChild.nextChild;\n }\n }\n \n //\n // This is a wrapper around the layoutNodeImpl function. It determines\n // whether the layout request is redundant and can be skipped.\n //\n // Parameters:\n // Input parameters are the same as layoutNodeImpl (see above)\n // Return parameter is true if layout was performed, false if skipped\n //\n function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection,\n widthMeasureMode, heightMeasureMode, performLayout, reason) {\n var layout = node.layout;\n\n var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) ||\n layout.lastParentDirection !== parentDirection;\n\n if (needToVisitNode) {\n // Invalidate the cached results.\n if (layout.cachedMeasurements !== undefined) {\n layout.cachedMeasurements = []; \n }\n if (layout.cachedLayout !== undefined) {\n layout.cachedLayout.widthMeasureMode = undefined;\n layout.cachedLayout.heightMeasureMode = undefined;\n }\n }\n\n var cachedResults;\n \n // Determine whether the results are already cached. We maintain a separate\n // cache for layouts and measurements. A layout operation modifies the positions\n // and dimensions for nodes in the subtree. The algorithm assumes that each node\n // gets layed out a maximum of one time per tree layout, but multiple measurements\n // may be required to resolve all of the flex dimensions.\n if (performLayout) {\n if (layout.cachedLayout &&\n layout.cachedLayout.availableWidth === availableWidth &&\n layout.cachedLayout.availableHeight === availableHeight &&\n layout.cachedLayout.widthMeasureMode === widthMeasureMode &&\n layout.cachedLayout.heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedLayout;\n }\n } else if (layout.cachedMeasurements) {\n for (var i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (layout.cachedMeasurements[i].availableWidth === availableWidth &&\n layout.cachedMeasurements[i].availableHeight === availableHeight &&\n layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode &&\n layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n \n if (!needToVisitNode && cachedResults !== undefined) {\n layout.measureWidth = cachedResults.computedWidth;\n layout.measureHeight = cachedResults.computedHeight;\n } else {\n layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);\n layout.lastParentDirection = parentDirection;\n \n if (cachedResults === undefined) {\n var newCacheEntry;\n if (performLayout) {\n // Use the single layout cache entry.\n if (layout.cachedLayout === undefined) {\n layout.cachedLayout = {};\n }\n newCacheEntry = layout.cachedLayout;\n } else {\n // Allocate a new measurement cache entry.\n if (layout.cachedMeasurements === undefined) {\n layout.cachedMeasurements = [];\n }\n newCacheEntry = {};\n layout.cachedMeasurements.push(newCacheEntry);\n }\n \n newCacheEntry.availableWidth = availableWidth;\n newCacheEntry.availableHeight = availableHeight;\n newCacheEntry.widthMeasureMode = widthMeasureMode;\n newCacheEntry.heightMeasureMode = heightMeasureMode;\n newCacheEntry.computedWidth = layout.measuredWidth;\n newCacheEntry.computedHeight = layout.measuredHeight;\n }\n }\n \n if (performLayout) {\n node.layout.width = node.layout.measuredWidth;\n node.layout.height = node.layout.measuredHeight;\n layout.shouldUpdate = true;\n }\n \n layout.generationCount = gCurrentGenerationCount;\n return (needToVisitNode || cachedResults === undefined);\n }\n \n function layoutNode(node, availableWidth, availableHeight, parentDirection) {\n // Increment the generation count. This will force the recursive routine to visit\n // all dirty nodes at least once. Subsequent visits will be skipped if the input\n // parameters don't change.\n gCurrentGenerationCount++;\n \n // If the caller didn't specify a height/width, use the dimensions\n // specified in the style.\n if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n }\n if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) {\n setPosition(node, node.layout.direction);\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file diff --git a/src/Layout.c b/src/Layout.c index 52acc085..b001efab 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -966,7 +966,8 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab remainingFreeSpace = -sizeConsumedOnCurrentLine; } - float remainingFreeSpaceAfterFlex = remainingFreeSpace; + float originalRemainingFreeSpace = remainingFreeSpace; + float deltaFreeSpace = 0; if (!canSkipFlex) { float childFlexBasis; @@ -989,7 +990,6 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // concerns because we know exactly how many passes it'll do. // First pass: detect the flex items whose min/max constraints trigger - float deltaFreeSpace = 0; float deltaFlexShrinkScaledFactors = 0; float deltaFlexGrowFactors = 0; currentRelativeChild = firstRelativeChild; @@ -1008,7 +1008,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; } } @@ -1024,7 +1024,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexGrowFactors -= flexGrowFactor; } } @@ -1036,9 +1036,9 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; totalFlexGrowFactors += deltaFlexGrowFactors; remainingFreeSpace += deltaFreeSpace; - remainingFreeSpaceAfterFlex = remainingFreeSpace; // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0; currentRelativeChild = firstRelativeChild; while (currentRelativeChild != NULL) { childFlexBasis = currentRelativeChild->layout.flex_basis; @@ -1062,7 +1062,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab } } - remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + deltaFreeSpace -= updatedMainSize - childFlexBasis; if (isMainAxisRow) { childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); @@ -1098,7 +1098,7 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab } } - remainingFreeSpace = remainingFreeSpaceAfterFlex; + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION diff --git a/src/Layout.js b/src/Layout.js index 72a68360..056ac0d9 100755 --- a/src/Layout.js +++ b/src/Layout.js @@ -916,7 +916,8 @@ var computeLayout = (function() { remainingFreeSpace = -sizeConsumedOnCurrentLine; } - var/*float*/ remainingFreeSpaceAfterFlex = remainingFreeSpace; + var/*float*/ originalRemainingFreeSpace = remainingFreeSpace; + var/*float*/ deltaFreeSpace = 0; if (!canSkipFlex) { var/*float*/ childFlexBasis; @@ -939,7 +940,6 @@ var computeLayout = (function() { // concerns because we know exactly how many passes it'll do. // First pass: detect the flex items whose min/max constraints trigger - var/*float*/ deltaFreeSpace = 0; var/*float*/ deltaFlexShrinkScaledFactors = 0; var/*float*/ deltaFlexGrowFactors = 0; currentRelativeChild = firstRelativeChild; @@ -958,7 +958,7 @@ var computeLayout = (function() { // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; } } @@ -974,7 +974,7 @@ var computeLayout = (function() { // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexGrowFactors -= flexGrowFactor; } } @@ -986,9 +986,9 @@ var computeLayout = (function() { totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; totalFlexGrowFactors += deltaFlexGrowFactors; remainingFreeSpace += deltaFreeSpace; - remainingFreeSpaceAfterFlex = remainingFreeSpace; // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0; currentRelativeChild = firstRelativeChild; while (currentRelativeChild !== undefined) { childFlexBasis = currentRelativeChild.layout.flexBasis; @@ -1012,7 +1012,7 @@ var computeLayout = (function() { } } - remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + deltaFreeSpace -= updatedMainSize - childFlexBasis; if (isMainAxisRow) { childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW); @@ -1048,7 +1048,7 @@ var computeLayout = (function() { } } - remainingFreeSpace = remainingFreeSpaceAfterFlex; + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION diff --git a/src/__tests__/Layout-test.c b/src/__tests__/Layout-test.c index 7a9b913d..18fe70dc 100644 --- a/src/__tests__/Layout-test.c +++ b/src/__tests__/Layout-test.c @@ -6923,6 +6923,93 @@ int main() test("should layout node with position absolute, top and left and min bounds", root_node, root_layout); } + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.justify_content = CSS_JUSTIFY_CENTER; + node_0->style.dimensions[CSS_WIDTH] = 1000; + node_0->style.dimensions[CSS_HEIGHT] = 1000; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = 1; + node_1->style.dimensions[CSS_HEIGHT] = 1000; + node_1->style.maxDimensions[CSS_WIDTH] = 600; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 1000; + node_0->layout.dimensions[CSS_HEIGHT] = 1000; + init_css_node_children(node_0, 1); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 200; + node_1->layout.dimensions[CSS_WIDTH] = 600; + node_1->layout.dimensions[CSS_HEIGHT] = 1000; + } + } + + test("should center flexible item with max size", root_node, root_layout); + } + + { + css_node_t *root_node = new_test_css_node(); + { + css_node_t *node_0 = root_node; + node_0->style.flex_direction = CSS_FLEX_DIRECTION_ROW; + node_0->style.dimensions[CSS_WIDTH] = 1000; + node_0->style.dimensions[CSS_HEIGHT] = 1000; + init_css_node_children(node_0, 2); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->style.flex = 1; + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 1000; + node_1 = node_0->get_child(node_0->context, 1); + node_1->style.flex = 1; + node_1->style.dimensions[CSS_WIDTH] = 100; + node_1->style.dimensions[CSS_HEIGHT] = 1000; + node_1->style.maxDimensions[CSS_WIDTH] = 200; + } + } + + css_node_t *root_layout = new_test_css_node(); + { + css_node_t *node_0 = root_layout; + node_0->layout.position[CSS_TOP] = 0; + node_0->layout.position[CSS_LEFT] = 0; + node_0->layout.dimensions[CSS_WIDTH] = 1000; + node_0->layout.dimensions[CSS_HEIGHT] = 1000; + init_css_node_children(node_0, 2); + { + css_node_t *node_1; + node_1 = node_0->get_child(node_0->context, 0); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 0; + node_1->layout.dimensions[CSS_WIDTH] = 800; + node_1->layout.dimensions[CSS_HEIGHT] = 1000; + node_1 = node_0->get_child(node_0->context, 1); + node_1->layout.position[CSS_TOP] = 0; + node_1->layout.position[CSS_LEFT] = 800; + node_1->layout.dimensions[CSS_WIDTH] = 200; + node_1->layout.dimensions[CSS_HEIGHT] = 1000; + } + } + + test("should correctly size flexible items with flex basis and a max width", root_node, root_layout); + } + { css_node_t *root_node = new_test_css_node(); { diff --git a/src/__tests__/Layout-test.js b/src/__tests__/Layout-test.js index d7edb768..738fd40c 100755 --- a/src/__tests__/Layout-test.js +++ b/src/__tests__/Layout-test.js @@ -2151,6 +2151,30 @@ describe('Layout', function() { ]} ); }); + + it('should center flexible item with max size', function() { + testLayout( + {style: {width: 1000, height: 1000, flexDirection: 'row', justifyContent: 'center'}, children: [ + {style: {flex: 1, maxWidth: 600, height: 1000}} + ]}, + {width: 1000, height: 1000, top: 0, left: 0, children: [ + {width: 600, height: 1000, top: 0, left: 200} + ]} + ); + }); + + it('should correctly size flexible items with flex basis and a max width', function() { + testLayout( + {style: {width: 1000, height: 1000, flexDirection: 'row'}, children: [ + {style: {flex: 1, width: 100, height: 1000}}, + {style: {flex: 1, width: 100, maxWidth: 200, height: 1000}} + ]}, + {width: 1000, height: 1000, top: 0, left: 0, children: [ + {width: 800, height: 1000, top: 0, left: 0}, + {width: 200, height: 1000, top: 0, left: 800} + ]} + ); + }); xit('should layout node with a nested sibling child with width', function() { testLayout( diff --git a/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs b/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs index 63fb0c9d..4950f230 100644 --- a/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs +++ b/src/csharp/Facebook.CSSLayout.Tests/LayoutEngineTest.cs @@ -7362,6 +7362,97 @@ public class LayoutEngineTest [Test] public void TestCase168() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.justifyContent = CSSJustify.Center; + node_0.style.dimensions[DIMENSION_WIDTH] = 1000; + node_0.style.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = 1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 1000; + node_1.style.maxWidth = 600; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 1000; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 200; + node_1.layout.dimensions[DIMENSION_WIDTH] = 600; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 1000; + } + } + + test("should center flexible item with max size", root_node, root_layout); + } + + [Test] + public void TestCase169() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.Row; + node_0.style.dimensions[DIMENSION_WIDTH] = 1000; + node_0.style.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = 1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 1000; + node_1 = node_0.getChildAt(1); + node_1.style.flex = 1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 1000; + node_1.style.maxWidth = 200; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 1000; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 800; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 1000; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 800; + node_1.layout.dimensions[DIMENSION_WIDTH] = 200; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 1000; + } + } + + test("should correctly size flexible items with flex basis and a max width", root_node, root_layout); + } + + [Test] + public void TestCase170() { TestCSSNode root_node = new TestCSSNode(); { @@ -7427,7 +7518,7 @@ public class LayoutEngineTest } [Test] - public void TestCase169() + public void TestCase171() { TestCSSNode root_node = new TestCSSNode(); { @@ -7499,7 +7590,7 @@ public class LayoutEngineTest } [Test] - public void TestCase170() + public void TestCase172() { TestCSSNode root_node = new TestCSSNode(); { @@ -7561,7 +7652,7 @@ public class LayoutEngineTest } [Test] - public void TestCase171() + public void TestCase173() { TestCSSNode root_node = new TestCSSNode(); { @@ -7655,7 +7746,7 @@ public class LayoutEngineTest } [Test] - public void TestCase172() + public void TestCase174() { TestCSSNode root_node = new TestCSSNode(); { @@ -7736,7 +7827,7 @@ public class LayoutEngineTest } [Test] - public void TestCase173() + public void TestCase175() { TestCSSNode root_node = new TestCSSNode(); { @@ -7776,7 +7867,7 @@ public class LayoutEngineTest } [Test] - public void TestCase174() + public void TestCase176() { TestCSSNode root_node = new TestCSSNode(); { @@ -7816,7 +7907,7 @@ public class LayoutEngineTest } [Test] - public void TestCase175() + public void TestCase177() { TestCSSNode root_node = new TestCSSNode(); { @@ -7856,7 +7947,7 @@ public class LayoutEngineTest } [Test] - public void TestCase176() + public void TestCase178() { TestCSSNode root_node = new TestCSSNode(); { @@ -7894,7 +7985,7 @@ public class LayoutEngineTest } [Test] - public void TestCase177() + public void TestCase179() { TestCSSNode root_node = new TestCSSNode(); { @@ -7933,7 +8024,7 @@ public class LayoutEngineTest } [Test] - public void TestCase178() + public void TestCase180() { TestCSSNode root_node = new TestCSSNode(); { @@ -7971,7 +8062,7 @@ public class LayoutEngineTest } [Test] - public void TestCase179() + public void TestCase181() { TestCSSNode root_node = new TestCSSNode(); { @@ -8010,7 +8101,7 @@ public class LayoutEngineTest } [Test] - public void TestCase180() + public void TestCase182() { TestCSSNode root_node = new TestCSSNode(); { @@ -8048,7 +8139,7 @@ public class LayoutEngineTest } [Test] - public void TestCase181() + public void TestCase183() { TestCSSNode root_node = new TestCSSNode(); { @@ -8087,7 +8178,7 @@ public class LayoutEngineTest } [Test] - public void TestCase182() + public void TestCase184() { TestCSSNode root_node = new TestCSSNode(); { @@ -8123,7 +8214,7 @@ public class LayoutEngineTest } [Test] - public void TestCase183() + public void TestCase185() { TestCSSNode root_node = new TestCSSNode(); { @@ -8191,7 +8282,7 @@ public class LayoutEngineTest } [Test] - public void TestCase184() + public void TestCase186() { TestCSSNode root_node = new TestCSSNode(); { @@ -8268,7 +8359,7 @@ public class LayoutEngineTest } [Test] - public void TestCase185() + public void TestCase187() { TestCSSNode root_node = new TestCSSNode(); { @@ -8340,7 +8431,7 @@ public class LayoutEngineTest } [Test] - public void TestCase186() + public void TestCase188() { TestCSSNode root_node = new TestCSSNode(); { @@ -8393,7 +8484,7 @@ public class LayoutEngineTest } [Test] - public void TestCase187() + public void TestCase189() { TestCSSNode root_node = new TestCSSNode(); { @@ -8483,7 +8574,7 @@ public class LayoutEngineTest } [Test] - public void TestCase188() + public void TestCase190() { TestCSSNode root_node = new TestCSSNode(); { @@ -8537,7 +8628,7 @@ public class LayoutEngineTest } [Test] - public void TestCase189() + public void TestCase191() { TestCSSNode root_node = new TestCSSNode(); { @@ -8591,7 +8682,7 @@ public class LayoutEngineTest } [Test] - public void TestCase190() + public void TestCase192() { TestCSSNode root_node = new TestCSSNode(); { @@ -8661,7 +8752,7 @@ public class LayoutEngineTest } [Test] - public void TestCase191() + public void TestCase193() { TestCSSNode root_node = new TestCSSNode(); { @@ -8731,7 +8822,7 @@ public class LayoutEngineTest } [Test] - public void TestCase192() + public void TestCase194() { TestCSSNode root_node = new TestCSSNode(); { @@ -8787,7 +8878,7 @@ public class LayoutEngineTest } [Test] - public void TestCase193() + public void TestCase195() { TestCSSNode root_node = new TestCSSNode(); { @@ -8842,7 +8933,7 @@ public class LayoutEngineTest } [Test] - public void TestCase194() + public void TestCase196() { TestCSSNode root_node = new TestCSSNode(); { @@ -8897,7 +8988,7 @@ public class LayoutEngineTest } [Test] - public void TestCase195() + public void TestCase197() { TestCSSNode root_node = new TestCSSNode(); { @@ -8968,7 +9059,7 @@ public class LayoutEngineTest } [Test] - public void TestCase196() + public void TestCase198() { TestCSSNode root_node = new TestCSSNode(); { @@ -9039,7 +9130,7 @@ public class LayoutEngineTest } [Test] - public void TestCase197() + public void TestCase199() { TestCSSNode root_node = new TestCSSNode(); { @@ -9096,7 +9187,7 @@ public class LayoutEngineTest } [Test] - public void TestCase198() + public void TestCase200() { TestCSSNode root_node = new TestCSSNode(); { @@ -9152,7 +9243,7 @@ public class LayoutEngineTest } [Test] - public void TestCase199() + public void TestCase201() { TestCSSNode root_node = new TestCSSNode(); { @@ -9208,7 +9299,7 @@ public class LayoutEngineTest } [Test] - public void TestCase200() + public void TestCase202() { TestCSSNode root_node = new TestCSSNode(); { @@ -9280,7 +9371,7 @@ public class LayoutEngineTest } [Test] - public void TestCase201() + public void TestCase203() { TestCSSNode root_node = new TestCSSNode(); { @@ -9352,7 +9443,7 @@ public class LayoutEngineTest } [Test] - public void TestCase202() + public void TestCase204() { TestCSSNode root_node = new TestCSSNode(); { @@ -9411,7 +9502,7 @@ public class LayoutEngineTest } [Test] - public void TestCase203() + public void TestCase205() { TestCSSNode root_node = new TestCSSNode(); { @@ -9484,7 +9575,7 @@ public class LayoutEngineTest } [Test] - public void TestCase204() + public void TestCase206() { TestCSSNode root_node = new TestCSSNode(); { @@ -9558,7 +9649,7 @@ public class LayoutEngineTest } [Test] - public void TestCase205() + public void TestCase207() { TestCSSNode root_node = new TestCSSNode(); { diff --git a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs index 175ec525..3d9924e5 100644 --- a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs +++ b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs @@ -784,7 +784,8 @@ namespace Facebook.CSSLayout remainingFreeSpace = -sizeConsumedOnCurrentLine; } - float remainingFreeSpaceAfterFlex = remainingFreeSpace; + float originalRemainingFreeSpace = remainingFreeSpace; + float deltaFreeSpace = 0; if (!canSkipFlex) { float childFlexBasis; @@ -807,7 +808,6 @@ namespace Facebook.CSSLayout // concerns because we know exactly how many passes it'll do. // First pass: detect the flex items whose min/max constraints trigger - float deltaFreeSpace = 0; float deltaFlexShrinkScaledFactors = 0; float deltaFlexGrowFactors = 0; currentRelativeChild = firstRelativeChild; @@ -826,7 +826,7 @@ namespace Facebook.CSSLayout // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; } } @@ -842,7 +842,7 @@ namespace Facebook.CSSLayout // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexGrowFactors -= flexGrowFactor; } } @@ -854,9 +854,9 @@ namespace Facebook.CSSLayout totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; totalFlexGrowFactors += deltaFlexGrowFactors; remainingFreeSpace += deltaFreeSpace; - remainingFreeSpaceAfterFlex = remainingFreeSpace; // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0; currentRelativeChild = firstRelativeChild; while (currentRelativeChild != null) { childFlexBasis = currentRelativeChild.layout.flexBasis; @@ -880,7 +880,7 @@ namespace Facebook.CSSLayout } } - remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + deltaFreeSpace -= updatedMainSize - childFlexBasis; if (isMainAxisRow) { childWidth = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); @@ -916,7 +916,7 @@ namespace Facebook.CSSLayout } } - remainingFreeSpace = remainingFreeSpaceAfterFlex; + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index 92965141..fa9c35ef 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -751,7 +751,8 @@ public class LayoutEngine { remainingFreeSpace = -sizeConsumedOnCurrentLine; } - float remainingFreeSpaceAfterFlex = remainingFreeSpace; + float originalRemainingFreeSpace = remainingFreeSpace; + float deltaFreeSpace = 0; if (!canSkipFlex) { float childFlexBasis; @@ -774,7 +775,6 @@ public class LayoutEngine { // concerns because we know exactly how many passes it'll do. // First pass: detect the flex items whose min/max constraints trigger - float deltaFreeSpace = 0; float deltaFlexShrinkScaledFactors = 0; float deltaFlexGrowFactors = 0; currentRelativeChild = firstRelativeChild; @@ -793,7 +793,7 @@ public class LayoutEngine { // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor; } } @@ -809,7 +809,7 @@ public class LayoutEngine { // By excluding this item's size and flex factor from remaining, this item's // min/max constraints should also trigger in the second pass resulting in the // item's size calculation being identical in the first and second passes. - deltaFreeSpace -= boundMainSize; + deltaFreeSpace -= boundMainSize - childFlexBasis; deltaFlexGrowFactors -= flexGrowFactor; } } @@ -821,9 +821,9 @@ public class LayoutEngine { totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors; totalFlexGrowFactors += deltaFlexGrowFactors; remainingFreeSpace += deltaFreeSpace; - remainingFreeSpaceAfterFlex = remainingFreeSpace; // Second pass: resolve the sizes of the flexible items + deltaFreeSpace = 0; currentRelativeChild = firstRelativeChild; while (currentRelativeChild != null) { childFlexBasis = currentRelativeChild.layout.flexBasis; @@ -847,7 +847,7 @@ public class LayoutEngine { } } - remainingFreeSpaceAfterFlex -= updatedMainSize - childFlexBasis; + deltaFreeSpace -= updatedMainSize - childFlexBasis; if (isMainAxisRow) { childWidth = updatedMainSize + (currentRelativeChild.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + currentRelativeChild.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW])); @@ -883,7 +883,7 @@ public class LayoutEngine { } } - remainingFreeSpace = remainingFreeSpaceAfterFlex; + remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace; // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION diff --git a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java index 54560437..4337dc20 100644 --- a/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java +++ b/src/java/tests/com/facebook/csslayout/LayoutEngineTest.java @@ -7365,6 +7365,97 @@ public class LayoutEngineTest { @Test public void testCase168() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.justifyContent = CSSJustify.CENTER; + node_0.style.dimensions[DIMENSION_WIDTH] = 1000; + node_0.style.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = 1; + node_1.style.dimensions[DIMENSION_HEIGHT] = 1000; + node_1.style.maxWidth = 600; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 1000; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 1); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 200; + node_1.layout.dimensions[DIMENSION_WIDTH] = 600; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 1000; + } + } + + test("should center flexible item with max size", root_node, root_layout); + } + + @Test + public void testCase169() + { + TestCSSNode root_node = new TestCSSNode(); + { + TestCSSNode node_0 = root_node; + node_0.style.flexDirection = CSSFlexDirection.ROW; + node_0.style.dimensions[DIMENSION_WIDTH] = 1000; + node_0.style.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.style.flex = 1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 1000; + node_1 = node_0.getChildAt(1); + node_1.style.flex = 1; + node_1.style.dimensions[DIMENSION_WIDTH] = 100; + node_1.style.dimensions[DIMENSION_HEIGHT] = 1000; + node_1.style.maxWidth = 200; + } + } + + TestCSSNode root_layout = new TestCSSNode(); + { + TestCSSNode node_0 = root_layout; + node_0.layout.position[POSITION_TOP] = 0; + node_0.layout.position[POSITION_LEFT] = 0; + node_0.layout.dimensions[DIMENSION_WIDTH] = 1000; + node_0.layout.dimensions[DIMENSION_HEIGHT] = 1000; + addChildren(node_0, 2); + { + TestCSSNode node_1; + node_1 = node_0.getChildAt(0); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 0; + node_1.layout.dimensions[DIMENSION_WIDTH] = 800; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 1000; + node_1 = node_0.getChildAt(1); + node_1.layout.position[POSITION_TOP] = 0; + node_1.layout.position[POSITION_LEFT] = 800; + node_1.layout.dimensions[DIMENSION_WIDTH] = 200; + node_1.layout.dimensions[DIMENSION_HEIGHT] = 1000; + } + } + + test("should correctly size flexible items with flex basis and a max width", root_node, root_layout); + } + + @Test + public void testCase170() { TestCSSNode root_node = new TestCSSNode(); { @@ -7430,7 +7521,7 @@ public class LayoutEngineTest { } @Test - public void testCase169() + public void testCase171() { TestCSSNode root_node = new TestCSSNode(); { @@ -7502,7 +7593,7 @@ public class LayoutEngineTest { } @Test - public void testCase170() + public void testCase172() { TestCSSNode root_node = new TestCSSNode(); { @@ -7564,7 +7655,7 @@ public class LayoutEngineTest { } @Test - public void testCase171() + public void testCase173() { TestCSSNode root_node = new TestCSSNode(); { @@ -7658,7 +7749,7 @@ public class LayoutEngineTest { } @Test - public void testCase172() + public void testCase174() { TestCSSNode root_node = new TestCSSNode(); { @@ -7739,7 +7830,7 @@ public class LayoutEngineTest { } @Test - public void testCase173() + public void testCase175() { TestCSSNode root_node = new TestCSSNode(); { @@ -7779,7 +7870,7 @@ public class LayoutEngineTest { } @Test - public void testCase174() + public void testCase176() { TestCSSNode root_node = new TestCSSNode(); { @@ -7819,7 +7910,7 @@ public class LayoutEngineTest { } @Test - public void testCase175() + public void testCase177() { TestCSSNode root_node = new TestCSSNode(); { @@ -7859,7 +7950,7 @@ public class LayoutEngineTest { } @Test - public void testCase176() + public void testCase178() { TestCSSNode root_node = new TestCSSNode(); { @@ -7897,7 +7988,7 @@ public class LayoutEngineTest { } @Test - public void testCase177() + public void testCase179() { TestCSSNode root_node = new TestCSSNode(); { @@ -7936,7 +8027,7 @@ public class LayoutEngineTest { } @Test - public void testCase178() + public void testCase180() { TestCSSNode root_node = new TestCSSNode(); { @@ -7974,7 +8065,7 @@ public class LayoutEngineTest { } @Test - public void testCase179() + public void testCase181() { TestCSSNode root_node = new TestCSSNode(); { @@ -8013,7 +8104,7 @@ public class LayoutEngineTest { } @Test - public void testCase180() + public void testCase182() { TestCSSNode root_node = new TestCSSNode(); { @@ -8051,7 +8142,7 @@ public class LayoutEngineTest { } @Test - public void testCase181() + public void testCase183() { TestCSSNode root_node = new TestCSSNode(); { @@ -8090,7 +8181,7 @@ public class LayoutEngineTest { } @Test - public void testCase182() + public void testCase184() { TestCSSNode root_node = new TestCSSNode(); { @@ -8126,7 +8217,7 @@ public class LayoutEngineTest { } @Test - public void testCase183() + public void testCase185() { TestCSSNode root_node = new TestCSSNode(); { @@ -8194,7 +8285,7 @@ public class LayoutEngineTest { } @Test - public void testCase184() + public void testCase186() { TestCSSNode root_node = new TestCSSNode(); { @@ -8271,7 +8362,7 @@ public class LayoutEngineTest { } @Test - public void testCase185() + public void testCase187() { TestCSSNode root_node = new TestCSSNode(); { @@ -8343,7 +8434,7 @@ public class LayoutEngineTest { } @Test - public void testCase186() + public void testCase188() { TestCSSNode root_node = new TestCSSNode(); { @@ -8396,7 +8487,7 @@ public class LayoutEngineTest { } @Test - public void testCase187() + public void testCase189() { TestCSSNode root_node = new TestCSSNode(); { @@ -8486,7 +8577,7 @@ public class LayoutEngineTest { } @Test - public void testCase188() + public void testCase190() { TestCSSNode root_node = new TestCSSNode(); { @@ -8540,7 +8631,7 @@ public class LayoutEngineTest { } @Test - public void testCase189() + public void testCase191() { TestCSSNode root_node = new TestCSSNode(); { @@ -8594,7 +8685,7 @@ public class LayoutEngineTest { } @Test - public void testCase190() + public void testCase192() { TestCSSNode root_node = new TestCSSNode(); { @@ -8664,7 +8755,7 @@ public class LayoutEngineTest { } @Test - public void testCase191() + public void testCase193() { TestCSSNode root_node = new TestCSSNode(); { @@ -8734,7 +8825,7 @@ public class LayoutEngineTest { } @Test - public void testCase192() + public void testCase194() { TestCSSNode root_node = new TestCSSNode(); { @@ -8790,7 +8881,7 @@ public class LayoutEngineTest { } @Test - public void testCase193() + public void testCase195() { TestCSSNode root_node = new TestCSSNode(); { @@ -8845,7 +8936,7 @@ public class LayoutEngineTest { } @Test - public void testCase194() + public void testCase196() { TestCSSNode root_node = new TestCSSNode(); { @@ -8900,7 +8991,7 @@ public class LayoutEngineTest { } @Test - public void testCase195() + public void testCase197() { TestCSSNode root_node = new TestCSSNode(); { @@ -8971,7 +9062,7 @@ public class LayoutEngineTest { } @Test - public void testCase196() + public void testCase198() { TestCSSNode root_node = new TestCSSNode(); { @@ -9042,7 +9133,7 @@ public class LayoutEngineTest { } @Test - public void testCase197() + public void testCase199() { TestCSSNode root_node = new TestCSSNode(); { @@ -9099,7 +9190,7 @@ public class LayoutEngineTest { } @Test - public void testCase198() + public void testCase200() { TestCSSNode root_node = new TestCSSNode(); { @@ -9155,7 +9246,7 @@ public class LayoutEngineTest { } @Test - public void testCase199() + public void testCase201() { TestCSSNode root_node = new TestCSSNode(); { @@ -9211,7 +9302,7 @@ public class LayoutEngineTest { } @Test - public void testCase200() + public void testCase202() { TestCSSNode root_node = new TestCSSNode(); { @@ -9283,7 +9374,7 @@ public class LayoutEngineTest { } @Test - public void testCase201() + public void testCase203() { TestCSSNode root_node = new TestCSSNode(); { @@ -9355,7 +9446,7 @@ public class LayoutEngineTest { } @Test - public void testCase202() + public void testCase204() { TestCSSNode root_node = new TestCSSNode(); { @@ -9414,7 +9505,7 @@ public class LayoutEngineTest { } @Test - public void testCase203() + public void testCase205() { TestCSSNode root_node = new TestCSSNode(); { @@ -9487,7 +9578,7 @@ public class LayoutEngineTest { } @Test - public void testCase204() + public void testCase206() { TestCSSNode root_node = new TestCSSNode(); { @@ -9561,7 +9652,7 @@ public class LayoutEngineTest { } @Test - public void testCase205() + public void testCase207() { TestCSSNode root_node = new TestCSSNode(); { From 8177bfe7027ec35be7cf0df362d26d0d3d2e4637 Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Tue, 10 May 2016 13:56:08 -0700 Subject: [PATCH 3/4] Heuristics for skipping calls to the measure function Introduced heuristics that enable css-layout to avoid calling measure functions under a number of conditions. This enables us to save time by skipping unnecessary measurements. --- dist/css-layout.h | 53 ++++++- dist/css-layout.jar | Bin 16323 -> 16326 bytes dist/css-layout.js | 64 +++++++- dist/css-layout.min.js | 2 +- dist/css-layout.min.js.map | 2 +- src/Layout-test-utils.js | 18 +++ src/Layout.c | 53 ++++++- src/Layout.js | 64 +++++++- src/__tests__/Layout-test.js | 147 ++++++++++++++++++ src/csharp/Facebook.CSSLayout/LayoutEngine.cs | 68 +++++++- .../com/facebook/csslayout/LayoutEngine.java | 57 ++++++- 11 files changed, 514 insertions(+), 14 deletions(-) diff --git a/dist/css-layout.h b/dist/css-layout.h index 4e0e1511..002eec12 100644 --- a/dist/css-layout.h +++ b/dist/css-layout.h @@ -1721,6 +1721,37 @@ static const char* getModeName(css_measure_mode_t mode, bool performLayout) { return performLayout? kLayoutModeNames[mode] : kMeasureModeNames[mode]; } +static bool canUseCachedMeasurement(float availableWidth, float availableHeight, + float marginRow, float marginColumn, + css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, + css_cached_measurement_t cachedLayout) { + + // Is it an exact match? + if (eq(cachedLayout.available_width, availableWidth) && + eq(cachedLayout.available_height, availableHeight) && + cachedLayout.width_measure_mode == widthMeasureMode && + cachedLayout.height_measure_mode == heightMeasureMode) { + return true; + } + + // If the width is an exact match, try a fuzzy match on the height. + if (cachedLayout.width_measure_mode == widthMeasureMode && + eq(cachedLayout.available_width, availableWidth) && + heightMeasureMode == CSS_MEASURE_MODE_EXACTLY && + eq(availableHeight - marginColumn, cachedLayout.computed_height)) { + return true; + } + + // If the height is an exact match, try a fuzzy match on the width. + if (cachedLayout.height_measure_mode == heightMeasureMode && + eq(cachedLayout.available_height, availableHeight) && + widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && + eq(availableWidth - marginRow, cachedLayout.computed_width)) { + return true; + } + + return false; +} // // This is a wrapper around the layoutNodeImpl function. It determines @@ -1753,7 +1784,27 @@ bool layoutNodeInternal(css_node_t* node, float availableWidth, float availableH // and dimensions for nodes in the subtree. The algorithm assumes that each node // gets layed out a maximum of one time per tree layout, but multiple measurements // may be required to resolve all of the flex dimensions. - if (performLayout) { + // We handle nodes with measure functions specially here because they are the most + // expensive to measure, so it's worth avoiding redundant measurements if at all possible. + if (isMeasureDefined(node)) { + float marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + float marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + + // First, try to use the layout cache. + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout->cached_layout)) { + cachedResults = &layout->cached_layout; + } else { + // Try to use the measurement cache. + for (int i = 0; i < layout->next_cached_measurements_index; i++) { + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout->cached_measurements[i])) { + cachedResults = &layout->cached_measurements[i]; + break; + } + } + } + } else if (performLayout) { if (eq(layout->cached_layout.available_width, availableWidth) && eq(layout->cached_layout.available_height, availableHeight) && layout->cached_layout.width_measure_mode == widthMeasureMode && diff --git a/dist/css-layout.jar b/dist/css-layout.jar index a13686964852c308a570cf5742ac435a08e608fc..90c02da277cff936784507a12e54360acb2ca9ea 100644 GIT binary patch delta 270 zcmX?Hf2^K2z?+#xgn@&DgP}cV(?nh?W)S7&ZFOY!Z0(F6;;L%TmuOi$7Z-gZt~!lj zYWAn1k420O0p9E!tPA+|I5RLXxBxN2kn?r3Hl7J(VwP&2wONIEA`5d<&Zf;f*w+~| zHv$=%)@Do~3nnkJwcrKP9&ke@Keshy25O!xZzs)kh+*;qJB`UHb~5rnb3CTpJ`|?K z!obid#lT<#v<(5ICeOF?23axrEl}G!cAz%B$rkp~3P8&|p1KH!U1Vfnc*M-WV1dvB WWW`&9&0k? delta 271 zcmX?Bf4H7Ez?+#xgn@&DgJBu->WREo%pl6k+gu~#hq$WR^Cemq&&5Tbh^tOxxSI8; z=wlHhLx49sM@yutqyqy3gCh_lOjxsT)yDIoOw4PUR&Q2kp2)(yka_jy-R$d(!HgVh zGbWJflb70B@B(QMxFM5Y*_z4&HGAx>KEL`4BLf3FD+7ZW&}anMKiSUCRRL(Q$CTTL z!n9Zz7#gJ*7;F#{ljE4hCVScQfb5+7!A_cKH9J(8ZL+n!G*~#&UYaS! P8Z11|UWsk8Eyx@I60?e(a):0}function h(a){if(V){if(0!==e(a))return 1}else if(e(a)<0)return 1;return 0}function i(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function j(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function k(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function l(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function m(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function n(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function o(a,b){return k(a,b)+m(a,b)}function p(a,b){return l(a,b)+n(a,b)}function q(a,b){return i(a,b)+j(a,b)}function r(a,b){return o(a,b)+p(a,b)}function s(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function t(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function u(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function v(a,b){if(b===ba){if(a===ca)return da;if(a===da)return ca}return a}function w(a,b){var c;return c=a.style.direction?a.style.direction:_,c===_&&(c=void 0===b?aa:b),c}function x(a){return a.style.flexDirection?a.style.flexDirection:ea}function y(a,b){return d(a)?v(ca,b):ea}function z(a){return a.style.position?a.style.position:pa}function A(a){return a.style.overflow?a.style.overflow:ra}function B(a){return z(a)===pa&&void 0!==a.style.flex&&0!==a.style.flex}function C(a){return"wrap"===a.style.flexWrap}function D(a,b){return a.layout[Aa[b]]+q(a,b)}function E(a,b){return void 0!==a.style[za[b]]&&a.style[za[b]]>=0}function F(a,b){return void 0!==a.layout[Aa[b]]&&a.layout[Aa[b]]>=0}function G(a,b){return void 0!==a.style[b]}function H(a){return void 0!==a.style.measure}function I(a,b){return void 0!==a.style[b]?a.style[b]:0}function J(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function K(a,b){return b>a?a:b}function L(a,b){return a>b?a:b}function M(a,b,c){return L(J(a,b,c),r(a,b))}function N(a,b,c){var d=z(b)===qa?0:b.layout[Aa[c]];b.layout[xa[c]]=a.layout[Aa[c]]-d-b.layout[ya[c]]}function O(a,b){return void 0!==a.style[wa[b]]?I(a,wa[b]):-I(a,xa[b])}function P(a,b){var c=v(x(a),b),d=y(c,b);a.layout[wa[c]]=i(a,c)+O(a,c),a.layout[xa[c]]=j(a,c)+O(a,c),a.layout[wa[d]]=i(a,d)+O(a,d),a.layout[xa[d]]=j(a,d)+O(a,d)}function Q(a,b){if(!a)throw new Error(b)}function R(a,d,e,k,l,O,R){Q(b(d)?l===ta:!0,"availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED"),Q(b(e)?O===ta:!0,"availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED");var T=r(a,ca),V=r(a,ea),W=q(a,ca),_=q(a,ea),aa=w(a,k);if(a.layout.direction=aa,H(a)){var ba=d-W-T,ra=e-_-V;if(l===ua&&O===ua)a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_);else if(0>=ba)a.layout.measuredWidth=M(a,ca,0),a.layout.measuredHeight=M(a,ea,0);else{var za=a.style.measure(ba,l,ra,O);a.layout.measuredWidth=M(a,ca,l===ta||l===va?za.width+T:d-W),a.layout.measuredHeight=M(a,ea,O===ta||O===va?za.height+V:e-_)}}else{var Ba=a.children.length;if(0===Ba)return a.layout.measuredWidth=M(a,ca,l===ta||l===va?T:d-W),void(a.layout.measuredHeight=M(a,ea,O===ta||O===va?V:e-_));if(!R){if(l===va&&0>=d&&O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,0));if(l===va&&0>=d)return a.layout.measuredWidth=M(a,ca,0),void(a.layout.measuredHeight=M(a,ea,b(e)?0:e-_));if(O===va&&0>=e)return a.layout.measuredWidth=M(a,ca,b(d)?0:d-W),void(a.layout.measuredHeight=M(a,ea,0));if(l===ua&&O===ua)return a.layout.measuredWidth=M(a,ca,d-W),void(a.layout.measuredHeight=M(a,ea,e-_))}var Ca,Da,Ea,Fa,Ga,Ha,Ia=v(x(a),aa),Ja=y(Ia,aa),Ka=c(Ia),La=s(a),Ma=C(a),Na=void 0,Oa=void 0,Pa=o(a,Ia),Qa=p(a,Ia),Ra=o(a,Ja),Sa=r(a,Ia),Ta=r(a,Ja),Ua=Ka?l:O,Va=Ka?O:l,Wa=d-W-T,Xa=e-_-V,Ya=Ka?Wa:Xa,Za=Ka?Xa:Wa;for(Da=0;Ba>Da;Da++){if(Ca=a.children[Da],R){var $a=w(Ca,aa);P(Ca,$a)}z(Ca)===qa?(void 0===Na&&(Na=Ca),void 0!==Oa&&(Oa.nextChild=Ca),Oa=Ca,Ca.nextChild=void 0):Ka&&E(Ca,ca)?Ca.layout.flexBasis=L(Ca.style.width,r(Ca,ca)):!Ka&&E(Ca,ea)?Ca.layout.flexBasis=L(Ca.style.height,r(Ca,ea)):f(Ca)||b(Ya)?(Ea=U,Fa=U,Ga=ta,Ha=ta,E(Ca,ca)&&(Ea=Ca.style.width+q(Ca,ca),Ga=ua),E(Ca,ea)&&(Fa=Ca.style.height+q(Ca,ea),Ha=ua),Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Ca,Ea,Fa,aa,Ga,Ha,!1,"measure"),Ca.layout.flexBasis=L(Ka?Ca.layout.measuredWidth:Ca.layout.measuredHeight,r(Ca,Ia))):Ca.layout.flexBasis=L(0,r(Ca,Ia))}for(var _a=0,ab=0,bb=0,cb=0,db=0;Ba>ab;){var eb=0,fb=0,gb=0,hb=0;Da=_a;for(var ib=void 0,jb=void 0;Ba>Da;){if(Ca=a.children[Da],Ca.lineIndex=bb,z(Ca)!==qa){var kb=Ca.layout.flexBasis+q(Ca,Ia);if(fb+kb>Ya&&Ma&&eb>0)break;fb+=kb,eb++,B(Ca)&&(gb+=g(Ca),hb+=h(Ca)*Ca.layout.flexBasis),void 0===ib&&(ib=Ca),void 0!==jb&&(jb.nextChild=Ca),jb=Ca,Ca.nextChild=void 0}Da++,ab++}var lb=!R&&Va===ua,mb=0,nb=0,ob=0;b(Ya)?0>fb&&(ob=-fb):ob=Ya-fb;var pb=ob,qb=0;if(!lb){var rb,sb,tb,ub,vb,wb=0,xb=0;for(jb=ib;void 0!==jb;)rb=jb.layout.flexBasis,0>ob?(sb=h(jb)*rb,0!==sb&&(ub=rb+ob/hb*sb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,wb-=sb))):ob>0&&(tb=g(jb),0!==tb&&(ub=rb+ob/gb*tb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,xb-=tb))),jb=jb.nextChild;for(hb+=wb,gb+=xb,ob+=qb,qb=0,jb=ib;void 0!==jb;){rb=jb.layout.flexBasis;var yb=rb;0>ob?(sb=h(jb)*rb,0!==sb&&(yb=M(jb,Ia,rb+ob/hb*sb))):ob>0&&(tb=g(jb),0!==tb&&(yb=M(jb,Ia,rb+ob/gb*tb))),qb-=yb-rb,Ka?(Ea=yb+q(jb,ca),Ga=ua,E(jb,ea)?(Fa=jb.style.height+q(jb,ea),Ha=ua):(Fa=Za,Ha=b(Fa)?ta:va)):(Fa=yb+q(jb,ea),Ha=ua,E(jb,ca)?(Ea=jb.style.width+q(jb,ca),Ga=ua):(Ea=Za,Ga=b(Ea)?ta:va));var zb=!E(jb,Ja)&&u(a,jb)===oa;S(jb,Ea,Fa,aa,Ga,Ha,R&&!zb,"flex"),jb=jb.nextChild}}ob=pb+qb,Ua===va&&(ob=0),La!==ga&&(La===ha?mb=ob/2:La===ia?mb=ob:La===ja?(ob=L(ob,0),nb=eb>1?ob/(eb-1):0):La===ka&&(nb=ob/eb,mb=nb/2));var Ab=Pa+mb,Bb=0;for(Da=_a;ab>Da;++Da)Ca=a.children[Da],z(Ca)===qa&&G(Ca,wa[Ia])?R&&(Ca.layout[ya[Ia]]=I(Ca,wa[Ia])+m(a,Ia)+i(Ca,Ia)):(R&&(Ca.layout[ya[Ia]]+=Ab),z(Ca)===pa&&(lb?(Ab+=nb+q(Ca,Ia)+Ca.layout.flexBasis,Bb=Za):(Ab+=nb+D(Ca,Ia),Bb=L(Bb,D(Ca,Ja)))));Ab+=Qa;var Cb=Za;if(Va!==ta&&Va!==va||(Cb=M(a,Ja,Bb+Ta)-Ta,Va===va&&(Cb=K(Cb,Za))),Ma||Va!==ua||(Bb=Za),Bb=M(a,Ja,Bb+Ta)-Ta,R)for(Da=_a;ab>Da;++Da)if(Ca=a.children[Da],z(Ca)===qa)G(Ca,wa[Ja])?Ca.layout[ya[Ja]]=I(Ca,wa[Ja])+m(a,Ja)+i(Ca,Ja):Ca.layout[ya[Ja]]=Ra+i(Ca,Ja);else{var Db=Ra,Eb=u(a,Ca);if(Eb===oa){Ea=Ca.layout.measuredWidth+q(Ca,ca),Fa=Ca.layout.measuredHeight+q(Ca,ea);var Fb=!1;Ka?(Fb=E(Ca,ea),Fa=Bb):(Fb=E(Ca,ca),Ea=Bb),Fb||(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,S(Ca,Ea,Fa,aa,Ga,Ha,!0,"stretch"))}else if(Eb!==la){var Gb=Cb-D(Ca,Ja);Db+=Eb===ma?Gb/2:Gb}Ca.layout[ya[Ja]]+=cb+Db}cb+=Bb,db=L(db,Ab),bb++,_a=ab,ab=_a}if(bb>1&&R&&!b(Za)){var Hb=Za-cb,Ib=0,Jb=Ra,Kb=t(a);Kb===na?Jb+=Hb:Kb===ma?Jb+=Hb/2:Kb===oa&&Za>cb&&(Ib=Hb/bb);var Lb=0;for(Da=0;bb>Da;++Da){var Mb,Nb=Lb,Ob=0;for(Mb=Nb;Ba>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){if(Ca.lineIndex!==Da)break;F(Ca,Ja)&&(Ob=L(Ob,Ca.layout[Aa[Ja]]+q(Ca,Ja)))}if(Lb=Mb,Ob+=Ib,R)for(Mb=Nb;Lb>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===pa){var Pb=u(a,Ca);Pb===la?Ca.layout[ya[Ja]]=Jb+i(Ca,Ja):Pb===na?Ca.layout[ya[Ja]]=Jb+Ob-j(Ca,Ja)-Ca.layout[Aa[Ja]]:Pb===ma?(Fa=Ca.layout[Aa[Ja]],Ca.layout[ya[Ja]]=Jb+(Ob-Fa)/2):Pb===oa&&(Ca.layout[ya[Ja]]=Jb+i(Ca,Ja))}Jb+=Ob}}if(a.layout.measuredWidth=M(a,ca,d-W),a.layout.measuredHeight=M(a,ea,e-_),Ua===ta?a.layout[Aa[Ia]]=M(a,Ia,db):Ua===va&&(a.layout[Aa[Ia]]=L(K(Ya+Sa,J(a,Ia,db)),Sa)),Va===ta?a.layout[Aa[Ja]]=M(a,Ja,cb+Ta):Va===va&&(a.layout[Aa[Ja]]=L(K(Za+Ta,J(a,Ja,cb+Ta)),Ta)),R){var Qb=!1,Rb=!1;if(Ia!==da&&Ia!==fa||(Qb=!0),Ja!==da&&Ja!==fa||(Rb=!0),Qb||Rb)for(Da=0;Ba>Da;++Da)Ca=a.children[Da],Qb&&N(a,Ca,Ia),Rb&&N(a,Ca,Ja)}for(Oa=Na;void 0!==Oa;)R&&(Ea=U,Fa=U,E(Oa,ca)?Ea=Oa.style.width+q(Oa,ca):G(Oa,X)&&G(Oa,Z)&&(Ea=a.layout.measuredWidth-(m(a,ca)+n(a,ca))-(Oa.style[X]+Oa.style[Z]),Ea=M(Oa,ca,Ea)),E(Oa,ea)?Fa=Oa.style.height+q(Oa,ea):G(Oa,Y)&&G(Oa,$)&&(Fa=a.layout.measuredHeight-(m(a,ea)+n(a,ea))-(Oa.style[Y]+Oa.style[$]),Fa=M(Oa,ea,Fa)),(b(Ea)||b(Fa))&&(Ga=b(Ea)?ta:ua,Ha=b(Fa)?ta:ua,Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=va),A(a)===sa&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=va),S(Oa,Ea,Fa,aa,Ga,Ha,!1,"abs-measure"),Ea=Oa.layout.measuredWidth+q(Oa,ca),Fa=Oa.layout.measuredHeight+q(Oa,ea)),S(Oa,Ea,Fa,aa,ua,ua,!0,"abs-layout"),G(Oa,xa[ca])&&!G(Oa,wa[ca])&&(Oa.layout[wa[ca]]=a.layout[Aa[ca]]-Oa.layout[Aa[ca]]-I(Oa,xa[ca])),G(Oa,xa[ea])&&!G(Oa,wa[ea])&&(Oa.layout[wa[ea]]=a.layout[Aa[ea]]-Oa.layout[Aa[ea]]-I(Oa,xa[ea]))),Oa=Oa.nextChild}}function S(a,b,c,d,e,f,g,h){var i=a.layout,j=a.isDirty&&i.generationCount!==W||i.lastParentDirection!==d;j&&(void 0!==i.cachedMeasurements&&(i.cachedMeasurements=[]),void 0!==i.cachedLayout&&(i.cachedLayout.widthMeasureMode=void 0,i.cachedLayout.heightMeasureMode=void 0));var k;if(g)i.cachedLayout&&i.cachedLayout.availableWidth===b&&i.cachedLayout.availableHeight===c&&i.cachedLayout.widthMeasureMode===e&&i.cachedLayout.heightMeasureMode===f&&(k=i.cachedLayout);else if(i.cachedMeasurements)for(var l=0,m=i.cachedMeasurements.length;m>l;l++)if(i.cachedMeasurements[l].availableWidth===b&&i.cachedMeasurements[l].availableHeight===c&&i.cachedMeasurements[l].widthMeasureMode===e&&i.cachedMeasurements[l].heightMeasureMode===f){k=i.cachedMeasurements[l];break}if(j||void 0===k){if(R(a,b,c,d,e,f,g),i.lastParentDirection=d,void 0===k){var n;g?(void 0===i.cachedLayout&&(i.cachedLayout={}),n=i.cachedLayout):(void 0===i.cachedMeasurements&&(i.cachedMeasurements=[]),n={},i.cachedMeasurements.push(n)),n.availableWidth=b,n.availableHeight=c,n.widthMeasureMode=e,n.heightMeasureMode=f,n.computedWidth=i.measuredWidth,n.computedHeight=i.measuredHeight}}else i.measureWidth=k.computedWidth,i.measureHeight=k.computedHeight;return g&&(a.layout.width=a.layout.measuredWidth,a.layout.height=a.layout.measuredHeight,i.shouldUpdate=!0),i.generationCount=W,j||void 0===k}function T(a,c,d,e){W++,b(c)&&E(a,ca)&&(c=a.style.width+q(a,ca)),b(d)&&E(a,ea)&&(d=a.style.height+q(a,ea));var f=b(c)?ta:ua,g=b(d)?ta:ua;S(a,c,d,e,f,g,!0,"initial")&&P(a,a.layout.direction)}var U,V=!1,W=0,X="left",Y="top",Z="right",$="bottom",_="inherit",aa="ltr",ba="rtl",ca="row",da="row-reverse",ea="column",fa="column-reverse",ga="flex-start",ha="center",ia="flex-end",ja="space-between",ka="space-around",la="flex-start",ma="center",na="flex-end",oa="stretch",pa="relative",qa="absolute",ra="visible",sa="hidden",ta="undefined",ua="exactly",va="at-most",wa={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},xa={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},ya={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},za={row:"width","row-reverse":"width",column:"height","column-reverse":"height"},Aa={row:"measuredWidth","row-reverse":"measuredWidth",column:"measuredHeight","column-reverse":"measuredHeight"};return{layoutNodeImpl:R,computeLayout:T,fillNodes:a}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); +!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.computeLayout=b()}(this,function(){var a=function(){function a(b){if(b.layout&&!b.isDirty||(b.layout={width:void 0,height:void 0,top:0,left:0,right:0,bottom:0}),b.style||(b.style={}),b.children||(b.children=[]),b.style.measure&&b.children&&b.children.length)throw new Error("Using custom measure function is supported only for leaf nodes.");return b.children.forEach(a),b}function b(a){return void 0===a||Number.isNaN(a)}function c(a){return a===da||a===ea}function d(a){return a===fa||a===ga}function e(a){return void 0===a.style.flex?0:a.style.flex}function f(a){return W?!0:e(a)<=0}function g(a){return e(a)>0?e(a):0}function h(a){if(W){if(0!==e(a))return 1}else if(e(a)<0)return 1;return 0}function i(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function j(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function k(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function l(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function m(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function n(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function o(a,b){return k(a,b)+m(a,b)}function p(a,b){return l(a,b)+n(a,b)}function q(a,b){return i(a,b)+j(a,b)}function r(a,b){return o(a,b)+p(a,b)}function s(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function t(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function u(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function v(a,b){if(b===ca){if(a===da)return ea;if(a===ea)return da}return a}function w(a,b){var c;return c=a.style.direction?a.style.direction:aa,c===aa&&(c=void 0===b?ba:b),c}function x(a){return a.style.flexDirection?a.style.flexDirection:fa}function y(a,b){return d(a)?v(da,b):fa}function z(a){return a.style.position?a.style.position:qa}function A(a){return a.style.overflow?a.style.overflow:sa}function B(a){return z(a)===qa&&void 0!==a.style.flex&&0!==a.style.flex}function C(a){return"wrap"===a.style.flexWrap}function D(a,b){return a.layout[Ba[b]]+q(a,b)}function E(a,b){return void 0!==a.style[Aa[b]]&&a.style[Aa[b]]>=0}function F(a,b){return void 0!==a.layout[Ba[b]]&&a.layout[Ba[b]]>=0}function G(a,b){return void 0!==a.style[b]}function H(a){return void 0!==a.style.measure}function I(a,b){return void 0!==a.style[b]?a.style[b]:0}function J(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function K(a,b){return b>a?a:b}function L(a,b){return a>b?a:b}function M(a,b,c){return L(J(a,b,c),r(a,b))}function N(a,b,c){var d=z(b)===ra?0:b.layout[Ba[c]];b.layout[ya[c]]=a.layout[Ba[c]]-d-b.layout[za[c]]}function O(a,b){return void 0!==a.style[xa[b]]?I(a,xa[b]):-I(a,ya[b])}function P(a,b){var c=v(x(a),b),d=y(c,b);a.layout[xa[c]]=i(a,c)+O(a,c),a.layout[ya[c]]=j(a,c)+O(a,c),a.layout[xa[d]]=i(a,d)+O(a,d),a.layout[ya[d]]=j(a,d)+O(a,d)}function Q(a,b){if(!a)throw new Error(b)}function R(a,d,e,k,l,O,R){Q(b(d)?l===ua:!0,"availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED"),Q(b(e)?O===ua:!0,"availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED");var S=r(a,da),U=r(a,fa),W=q(a,da),X=q(a,fa),aa=w(a,k);if(a.layout.direction=aa,H(a)){var ba=d-W-S,ca=e-X-U;if(l===va&&O===va)a.layout.measuredWidth=M(a,da,d-W),a.layout.measuredHeight=M(a,fa,e-X);else if(0>=ba)a.layout.measuredWidth=M(a,da,0),a.layout.measuredHeight=M(a,fa,0);else{var sa=a.style.measure(ba,l,ca,O);a.layout.measuredWidth=M(a,da,l===ua||l===wa?sa.width+S:d-W),a.layout.measuredHeight=M(a,fa,O===ua||O===wa?sa.height+U:e-X)}}else{var Aa=a.children.length;if(0===Aa)return a.layout.measuredWidth=M(a,da,l===ua||l===wa?S:d-W),void(a.layout.measuredHeight=M(a,fa,O===ua||O===wa?U:e-X));if(!R){if(l===wa&&0>=d&&O===wa&&0>=e)return a.layout.measuredWidth=M(a,da,0),void(a.layout.measuredHeight=M(a,fa,0));if(l===wa&&0>=d)return a.layout.measuredWidth=M(a,da,0),void(a.layout.measuredHeight=M(a,fa,b(e)?0:e-X));if(O===wa&&0>=e)return a.layout.measuredWidth=M(a,da,b(d)?0:d-W),void(a.layout.measuredHeight=M(a,fa,0));if(l===va&&O===va)return a.layout.measuredWidth=M(a,da,d-W),void(a.layout.measuredHeight=M(a,fa,e-X))}var Ca,Da,Ea,Fa,Ga,Ha,Ia=v(x(a),aa),Ja=y(Ia,aa),Ka=c(Ia),La=s(a),Ma=C(a),Na=void 0,Oa=void 0,Pa=o(a,Ia),Qa=p(a,Ia),Ra=o(a,Ja),Sa=r(a,Ia),Ta=r(a,Ja),Ua=Ka?l:O,Va=Ka?O:l,Wa=d-W-S,Xa=e-X-U,Ya=Ka?Wa:Xa,Za=Ka?Xa:Wa;for(Da=0;Aa>Da;Da++){if(Ca=a.children[Da],R){var $a=w(Ca,aa);P(Ca,$a)}z(Ca)===ra?(void 0===Na&&(Na=Ca),void 0!==Oa&&(Oa.nextChild=Ca),Oa=Ca,Ca.nextChild=void 0):Ka&&E(Ca,da)?Ca.layout.flexBasis=L(Ca.style.width,r(Ca,da)):!Ka&&E(Ca,fa)?Ca.layout.flexBasis=L(Ca.style.height,r(Ca,fa)):f(Ca)||b(Ya)?(Ea=V,Fa=V,Ga=ua,Ha=ua,E(Ca,da)&&(Ea=Ca.style.width+q(Ca,da),Ga=va),E(Ca,fa)&&(Fa=Ca.style.height+q(Ca,fa),Ha=va),Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=wa),A(a)===ta&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=wa),T(Ca,Ea,Fa,aa,Ga,Ha,!1,"measure"),Ca.layout.flexBasis=L(Ka?Ca.layout.measuredWidth:Ca.layout.measuredHeight,r(Ca,Ia))):Ca.layout.flexBasis=L(0,r(Ca,Ia))}for(var _a=0,ab=0,bb=0,cb=0,db=0;Aa>ab;){var eb=0,fb=0,gb=0,hb=0;Da=_a;for(var ib=void 0,jb=void 0;Aa>Da;){if(Ca=a.children[Da],Ca.lineIndex=bb,z(Ca)!==ra){var kb=Ca.layout.flexBasis+q(Ca,Ia);if(fb+kb>Ya&&Ma&&eb>0)break;fb+=kb,eb++,B(Ca)&&(gb+=g(Ca),hb+=h(Ca)*Ca.layout.flexBasis),void 0===ib&&(ib=Ca),void 0!==jb&&(jb.nextChild=Ca),jb=Ca,Ca.nextChild=void 0}Da++,ab++}var lb=!R&&Va===va,mb=0,nb=0,ob=0;b(Ya)?0>fb&&(ob=-fb):ob=Ya-fb;var pb=ob,qb=0;if(!lb){var rb,sb,tb,ub,vb,wb=0,xb=0;for(jb=ib;void 0!==jb;)rb=jb.layout.flexBasis,0>ob?(sb=h(jb)*rb,0!==sb&&(ub=rb+ob/hb*sb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,wb-=sb))):ob>0&&(tb=g(jb),0!==tb&&(ub=rb+ob/gb*tb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,xb-=tb))),jb=jb.nextChild;for(hb+=wb,gb+=xb,ob+=qb,qb=0,jb=ib;void 0!==jb;){rb=jb.layout.flexBasis;var yb=rb;0>ob?(sb=h(jb)*rb,0!==sb&&(yb=M(jb,Ia,rb+ob/hb*sb))):ob>0&&(tb=g(jb),0!==tb&&(yb=M(jb,Ia,rb+ob/gb*tb))),qb-=yb-rb,Ka?(Ea=yb+q(jb,da),Ga=va,E(jb,fa)?(Fa=jb.style.height+q(jb,fa),Ha=va):(Fa=Za,Ha=b(Fa)?ua:wa)):(Fa=yb+q(jb,fa),Ha=va,E(jb,da)?(Ea=jb.style.width+q(jb,da),Ga=va):(Ea=Za,Ga=b(Ea)?ua:wa));var zb=!E(jb,Ja)&&u(a,jb)===pa;T(jb,Ea,Fa,aa,Ga,Ha,R&&!zb,"flex"),jb=jb.nextChild}}ob=pb+qb,Ua===wa&&(ob=0),La!==ha&&(La===ia?mb=ob/2:La===ja?mb=ob:La===ka?(ob=L(ob,0),nb=eb>1?ob/(eb-1):0):La===la&&(nb=ob/eb,mb=nb/2));var Ab=Pa+mb,Bb=0;for(Da=_a;ab>Da;++Da)Ca=a.children[Da],z(Ca)===ra&&G(Ca,xa[Ia])?R&&(Ca.layout[za[Ia]]=I(Ca,xa[Ia])+m(a,Ia)+i(Ca,Ia)):(R&&(Ca.layout[za[Ia]]+=Ab),z(Ca)===qa&&(lb?(Ab+=nb+q(Ca,Ia)+Ca.layout.flexBasis,Bb=Za):(Ab+=nb+D(Ca,Ia),Bb=L(Bb,D(Ca,Ja)))));Ab+=Qa;var Cb=Za;if(Va!==ua&&Va!==wa||(Cb=M(a,Ja,Bb+Ta)-Ta,Va===wa&&(Cb=K(Cb,Za))),Ma||Va!==va||(Bb=Za),Bb=M(a,Ja,Bb+Ta)-Ta,R)for(Da=_a;ab>Da;++Da)if(Ca=a.children[Da],z(Ca)===ra)G(Ca,xa[Ja])?Ca.layout[za[Ja]]=I(Ca,xa[Ja])+m(a,Ja)+i(Ca,Ja):Ca.layout[za[Ja]]=Ra+i(Ca,Ja);else{var Db=Ra,Eb=u(a,Ca);if(Eb===pa){Ea=Ca.layout.measuredWidth+q(Ca,da),Fa=Ca.layout.measuredHeight+q(Ca,fa);var Fb=!1;Ka?(Fb=E(Ca,fa),Fa=Bb):(Fb=E(Ca,da),Ea=Bb),Fb||(Ga=b(Ea)?ua:va,Ha=b(Fa)?ua:va,T(Ca,Ea,Fa,aa,Ga,Ha,!0,"stretch"))}else if(Eb!==ma){var Gb=Cb-D(Ca,Ja);Db+=Eb===na?Gb/2:Gb}Ca.layout[za[Ja]]+=cb+Db}cb+=Bb,db=L(db,Ab),bb++,_a=ab,ab=_a}if(bb>1&&R&&!b(Za)){var Hb=Za-cb,Ib=0,Jb=Ra,Kb=t(a);Kb===oa?Jb+=Hb:Kb===na?Jb+=Hb/2:Kb===pa&&Za>cb&&(Ib=Hb/bb);var Lb=0;for(Da=0;bb>Da;++Da){var Mb,Nb=Lb,Ob=0;for(Mb=Nb;Aa>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===qa){if(Ca.lineIndex!==Da)break;F(Ca,Ja)&&(Ob=L(Ob,Ca.layout[Ba[Ja]]+q(Ca,Ja)))}if(Lb=Mb,Ob+=Ib,R)for(Mb=Nb;Lb>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===qa){var Pb=u(a,Ca);Pb===ma?Ca.layout[za[Ja]]=Jb+i(Ca,Ja):Pb===oa?Ca.layout[za[Ja]]=Jb+Ob-j(Ca,Ja)-Ca.layout[Ba[Ja]]:Pb===na?(Fa=Ca.layout[Ba[Ja]],Ca.layout[za[Ja]]=Jb+(Ob-Fa)/2):Pb===pa&&(Ca.layout[za[Ja]]=Jb+i(Ca,Ja))}Jb+=Ob}}if(a.layout.measuredWidth=M(a,da,d-W),a.layout.measuredHeight=M(a,fa,e-X),Ua===ua?a.layout[Ba[Ia]]=M(a,Ia,db):Ua===wa&&(a.layout[Ba[Ia]]=L(K(Ya+Sa,J(a,Ia,db)),Sa)),Va===ua?a.layout[Ba[Ja]]=M(a,Ja,cb+Ta):Va===wa&&(a.layout[Ba[Ja]]=L(K(Za+Ta,J(a,Ja,cb+Ta)),Ta)),R){var Qb=!1,Rb=!1;if(Ia!==ea&&Ia!==ga||(Qb=!0),Ja!==ea&&Ja!==ga||(Rb=!0),Qb||Rb)for(Da=0;Aa>Da;++Da)Ca=a.children[Da],Qb&&N(a,Ca,Ia),Rb&&N(a,Ca,Ja)}for(Oa=Na;void 0!==Oa;)R&&(Ea=V,Fa=V,E(Oa,da)?Ea=Oa.style.width+q(Oa,da):G(Oa,Y)&&G(Oa,$)&&(Ea=a.layout.measuredWidth-(m(a,da)+n(a,da))-(Oa.style[Y]+Oa.style[$]),Ea=M(Oa,da,Ea)),E(Oa,fa)?Fa=Oa.style.height+q(Oa,fa):G(Oa,Z)&&G(Oa,_)&&(Fa=a.layout.measuredHeight-(m(a,fa)+n(a,fa))-(Oa.style[Z]+Oa.style[_]),Fa=M(Oa,fa,Fa)),(b(Ea)||b(Fa))&&(Ga=b(Ea)?ua:va,Ha=b(Fa)?ua:va,Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=wa),A(a)===ta&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=wa),T(Oa,Ea,Fa,aa,Ga,Ha,!1,"abs-measure"),Ea=Oa.layout.measuredWidth+q(Oa,da),Fa=Oa.layout.measuredHeight+q(Oa,fa)),T(Oa,Ea,Fa,aa,va,va,!0,"abs-layout"),G(Oa,ya[da])&&!G(Oa,xa[da])&&(Oa.layout[xa[da]]=a.layout[Ba[da]]-Oa.layout[Ba[da]]-I(Oa,ya[da])),G(Oa,ya[fa])&&!G(Oa,xa[fa])&&(Oa.layout[xa[fa]]=a.layout[Ba[fa]]-Oa.layout[Ba[fa]]-I(Oa,ya[fa]))),Oa=Oa.nextChild}}function S(a,b,c,d,e,f,g){return g.availableWidth===a&&g.availableHeight===b&&g.widthMeasureMode===e&&g.heightMeasureMode===f?!0:g.availableWidth===a&&g.widthMeasureMode===e&&f===va&&b-d===g.computedHeight?!0:g.availableHeight===b&&g.heightMeasureMode===f&&e===va&&a-c===g.computedWidth}function T(a,b,c,d,e,f,g,h){var i=a.layout,j=a.isDirty&&i.generationCount!==X||i.lastParentDirection!==d;j&&(void 0!==i.cachedMeasurements&&(i.cachedMeasurements=[]),void 0!==i.cachedLayout&&(i.cachedLayout.widthMeasureMode=void 0,i.cachedLayout.heightMeasureMode=void 0));var k,l,m;if(H(a)){var n=q(a,da),o=q(a,fa);if(i.cachedLayout&&S(b,c,n,o,e,f,i.cachedLayout))m=i.cachedLayout;else if(i.cachedMeasurements)for(k=0,l=i.cachedMeasurements.length;l>k;k++)if(S(b,c,n,o,e,f,i.cachedMeasurements[k])){m=i.cachedMeasurements[k];break}}else if(g)i.cachedLayout&&i.cachedLayout.availableWidth===b&&i.cachedLayout.availableHeight===c&&i.cachedLayout.widthMeasureMode===e&&i.cachedLayout.heightMeasureMode===f&&(m=i.cachedLayout);else if(i.cachedMeasurements)for(k=0,l=i.cachedMeasurements.length;l>k;k++)if(i.cachedMeasurements[k].availableWidth===b&&i.cachedMeasurements[k].availableHeight===c&&i.cachedMeasurements[k].widthMeasureMode===e&&i.cachedMeasurements[k].heightMeasureMode===f){m=i.cachedMeasurements[k];break}if(j||void 0===m){if(R(a,b,c,d,e,f,g),i.lastParentDirection=d,void 0===m){var p;g?(void 0===i.cachedLayout&&(i.cachedLayout={}),p=i.cachedLayout):(void 0===i.cachedMeasurements&&(i.cachedMeasurements=[]),p={},i.cachedMeasurements.push(p)),p.availableWidth=b,p.availableHeight=c,p.widthMeasureMode=e,p.heightMeasureMode=f,p.computedWidth=i.measuredWidth,p.computedHeight=i.measuredHeight}}else i.measureWidth=m.computedWidth,i.measureHeight=m.computedHeight;return g&&(a.layout.width=a.layout.measuredWidth,a.layout.height=a.layout.measuredHeight,i.shouldUpdate=!0),i.generationCount=X,j||void 0===m}function U(a,c,d,e){X++,b(c)&&E(a,da)&&(c=a.style.width+q(a,da)),b(d)&&E(a,fa)&&(d=a.style.height+q(a,fa));var f=b(c)?ua:va,g=b(d)?ua:va;T(a,c,d,e,f,g,!0,"initial")&&P(a,a.layout.direction)}var V,W=!1,X=0,Y="left",Z="top",$="right",_="bottom",aa="inherit",ba="ltr",ca="rtl",da="row",ea="row-reverse",fa="column",ga="column-reverse",ha="flex-start",ia="center",ja="flex-end",ka="space-between",la="space-around",ma="flex-start",na="center",oa="flex-end",pa="stretch",qa="relative",ra="absolute",sa="visible",ta="hidden",ua="undefined",va="exactly",wa="at-most",xa={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},ya={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},za={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},Aa={row:"width","row-reverse":"width",column:"height","column-reverse":"height"},Ba={row:"measuredWidth","row-reverse":"measuredWidth",column:"measuredHeight","column-reverse":"measuredHeight"};return{layoutNodeImpl:R,computeLayout:U,fillNodes:a,canUseCachedMeasurement:S}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); //# sourceMappingURL=css-layout.min.js.map \ No newline at end of file diff --git a/dist/css-layout.min.js.map b/dist/css-layout.min.js.map index 4f9fb2e9..52b7586f 100644 --- a/dist/css-layout.min.js.map +++ b/dist/css-layout.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","Number","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getFlex","flex","isFlexBasisAuto","POSITIVE_FLEX_IS_AUTO","getFlexGrowFactor","getFlexShrinkFactor","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","CSS_POSITION_RELATIVE","getOverflow","overflow","CSS_OVERFLOW_VISIBLE","isFlex","isFlexWrap","flexWrap","getDimWithMargin","measuredDim","isStyleDimDefined","dim","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxisWithinMinAndMax","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fminf","a","b","fmaxf","boundAxis","setTrailingPosition","size","CSS_POSITION_ABSOLUTE","trailing","getRelativePosition","leading","setPosition","mainAxis","crossAxis","assert","condition","message","layoutNodeImpl","availableWidth","availableHeight","widthMeasureMode","heightMeasureMode","performLayout","CSS_MEASURE_MODE_UNDEFINED","paddingAndBorderAxisRow","paddingAndBorderAxisColumn","marginAxisRow","marginAxisColumn","innerWidth","innerHeight","CSS_MEASURE_MODE_EXACTLY","measuredWidth","measuredHeight","measureDim","CSS_MEASURE_MODE_AT_MOST","childCount","i","childWidth","childHeight","childWidthMeasureMode","childHeightMeasureMode","isMainAxisRow","isNodeFlexWrap","firstAbsoluteChild","currentAbsoluteChild","leadingPaddingAndBorderMain","trailingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","measureModeMainDim","measureModeCrossDim","availableInnerWidth","availableInnerHeight","availableInnerMainDim","availableInnerCrossDim","childDirection","nextChild","flexBasis","CSS_UNDEFINED","CSS_OVERFLOW_HIDDEN","layoutNodeInternal","startOfLineIndex","endOfLineIndex","lineCount","totalLineCrossDim","maxLineMainDim","itemsOnLine","sizeConsumedOnCurrentLine","totalFlexGrowFactors","totalFlexShrinkScaledFactors","firstRelativeChild","currentRelativeChild","lineIndex","outerFlexBasis","canSkipFlex","leadingMainDim","betweenMainDim","remainingFreeSpace","originalRemainingFreeSpace","deltaFreeSpace","childFlexBasis","flexShrinkScaledFactor","flexGrowFactor","baseMainSize","boundMainSize","deltaFlexShrinkScaledFactors","deltaFlexGrowFactors","updatedMainSize","requiresStretchLayout","CSS_ALIGN_STRETCH","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","mainDim","crossDim","containerCrossAxis","leadingCrossDim","alignItem","isCrossSizeDefinite","CSS_ALIGN_FLEX_START","remainingCrossDim","CSS_ALIGN_CENTER","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","j","startIndex","lineHeight","alignContentAlignItem","needsMainTrailingPos","needsCrossTrailingPos","CSS_LEFT","CSS_RIGHT","CSS_TOP","CSS_BOTTOM","reason","needToVisitNode","generationCount","gCurrentGenerationCount","lastParentDirection","cachedMeasurements","cachedLayout","cachedResults","len","newCacheEntry","push","computedWidth","computedHeight","measureWidth","measureHeight","shouldUpdate","layoutNode"],"mappings":"CAKC,SAASA,EAAMC,GACQ,kBAAXC,SAAyBA,OAAOC,IAEzCD,UAAWD,GACiB,gBAAZG,SAIhBC,OAAOD,QAAUH,IAGjBD,EAAKM,cAAgBL,KAEvBM,KAAM,WAUR,GAAID,GAAgB,WA6ElB,QAASE,GAAUC,GAoBjB,GAnBKA,EAAKC,SAAUD,EAAKE,UACvBF,EAAKC,QACHE,MAAOC,OACPC,OAAQD,OACRE,IAAK,EACLC,KAAM,EACNC,MAAO,EACPC,OAAQ,IAIPT,EAAKU,QACRV,EAAKU,UAGFV,EAAKW,WACRX,EAAKW,aAGHX,EAAKU,MAAME,SAAWZ,EAAKW,UAAYX,EAAKW,SAASE,OACvD,KAAM,IAAIC,OAAM,kEAIlB,OADAd,GAAKW,SAASI,QAAQhB,GACfC,EAGT,QAASgB,GAAYC,GACnB,MAAiBb,UAAVa,GAAuBC,OAAOC,MAAMF,GAG7C,QAASG,GAAeC,GACtB,MAAOA,KAAkBC,IAClBD,IAAkBE,GAG3B,QAASC,GAAkBH,GACzB,MAAOA,KAAkBI,IAClBJ,IAAkBK,GAG3B,QAASC,GAAQ3B,GACf,MAAwBI,UAApBJ,EAAKU,MAAMkB,KACN,EAEF5B,EAAKU,MAAMkB,KAGpB,QAASC,GAAgB7B,GACvB,MAAI8B,IAEK,EAGAH,EAAQ3B,IAAS,EAI5B,QAAS+B,GAAkB/B,GAEzB,MAAI2B,GAAQ3B,GAAQ,EACX2B,EAAQ3B,GAEV,EAGT,QAASgC,GAAoBhC,GAC3B,GAAI8B,GAEF,GAAsB,IAAlBH,EAAQ3B,GACV,MAAO,OAIT,IAAI2B,EAAQ3B,GAAQ,EAClB,MAAO,EAGX,OAAO,GAGT,QAASiC,GAAiBjC,EAAMkC,GAC9B,GAA+B9B,SAA3BJ,EAAKU,MAAMyB,aAA6Bf,EAAec,GACzD,MAAOlC,GAAKU,MAAMyB,WAGpB,IAAIlB,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM0B,UAAc,MACxD,KAAK,cAAkBnB,EAAQjB,EAAKU,MAAM2B,WAAc,MACxD,KAAK,SAAkBpB,EAAQjB,EAAKU,MAAM4B,SAAc,MACxD,KAAK,iBAAkBrB,EAAQjB,EAAKU,MAAM6B,aAG5C,MAAcnC,UAAVa,EACKA,EAGiBb,SAAtBJ,EAAKU,MAAM8B,OACNxC,EAAKU,MAAM8B,OAGb,EAGT,QAASC,GAAkBzC,EAAMkC,GAC/B,GAA6B9B,SAAzBJ,EAAKU,MAAMgC,WAA2BtB,EAAec,GACvD,MAAOlC,GAAKU,MAAMgC,SAGpB,IAAIzB,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM2B,WAAc,MACxD,KAAK,cAAkBpB,EAAQjB,EAAKU,MAAM0B,UAAc,MACxD,KAAK,SAAkBnB,EAAQjB,EAAKU,MAAM6B,YAAc,MACxD,KAAK,iBAAkBtB,EAAQjB,EAAKU,MAAM4B,UAG5C,MAAa,OAATrB,EACKA,EAGiBb,SAAtBJ,EAAKU,MAAM8B,OACNxC,EAAKU,MAAM8B,OAGb,EAGT,QAASG,GAAkB3C,EAAMkC,GAC/B,GAAgC9B,SAA5BJ,EAAKU,MAAMkC,cAA8B5C,EAAKU,MAAMkC,cAAgB,GACjExB,EAAec,GACpB,MAAOlC,GAAKU,MAAMkC,YAGpB,IAAI3B,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAMmC,WAAe,MACzD,KAAK,cAAkB5B,EAAQjB,EAAKU,MAAMoC,YAAe,MACzD,KAAK,SAAkB7B,EAAQjB,EAAKU,MAAMqC,UAAe,MACzD,KAAK,iBAAkB9B,EAAQjB,EAAKU,MAAMsC,cAG5C,MAAa,OAAT/B,GAAiBA,GAAS,EACrBA,EAGkBb,SAAvBJ,EAAKU,MAAMuC,SAAyBjD,EAAKU,MAAMuC,SAAW,EACrDjD,EAAKU,MAAMuC,QAGb,EAGT,QAASC,GAAmBlD,EAAMkC,GAChC,GAA8B9B,SAA1BJ,EAAKU,MAAMyC,YAA4BnD,EAAKU,MAAMyC,YAAc,GAC7D/B,EAAec,GACpB,MAAOlC,GAAKU,MAAMyC,UAGpB,IAAIlC,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAMoC,YAAe,MACzD,KAAK,cAAkB7B,EAAQjB,EAAKU,MAAMmC,WAAe,MACzD,KAAK,SAAkB5B,EAAQjB,EAAKU,MAAMsC,aAAe,MACzD,KAAK,iBAAkB/B,EAAQjB,EAAKU,MAAMqC,WAG5C,MAAa,OAAT9B,GAAiBA,GAAS,EACrBA,EAGkBb,SAAvBJ,EAAKU,MAAMuC,SAAyBjD,EAAKU,MAAMuC,SAAW,EACrDjD,EAAKU,MAAMuC,QAGb,EAGT,QAASG,GAAiBpD,EAAMkC,GAC9B,GAAoC9B,SAAhCJ,EAAKU,MAAM2C,kBAAkCrD,EAAKU,MAAM2C,kBAAoB,GACzEjC,EAAec,GACpB,MAAOlC,GAAKU,MAAM2C,gBAGpB,IAAIpC,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM4C,eAAmB,MAC7D,KAAK,cAAkBrC,EAAQjB,EAAKU,MAAM6C,gBAAmB,MAC7D,KAAK,SAAkBtC,EAAQjB,EAAKU,MAAM8C,cAAmB,MAC7D,KAAK,iBAAkBvC,EAAQjB,EAAKU,MAAM+C,kBAG5C,MAAa,OAATxC,GAAiBA,GAAS,EACrBA,EAGsBb,SAA3BJ,EAAKU,MAAMgD,aAA6B1D,EAAKU,MAAMgD,aAAe,EAC7D1D,EAAKU,MAAMgD,YAGb,EAGT,QAASC,GAAkB3D,EAAMkC,GAC/B,GAAkC9B,SAA9BJ,EAAKU,MAAMkD,gBAAgC5D,EAAKU,MAAMkD,gBAAkB,GACrExC,EAAec,GACpB,MAAOlC,GAAKU,MAAMkD,cAGpB,IAAI3C,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM6C,gBAAmB,MAC7D,KAAK,cAAkBtC,EAAQjB,EAAKU,MAAM4C,eAAmB,MAC7D,KAAK,SAAkBrC,EAAQjB,EAAKU,MAAM+C,iBAAmB,MAC7D,KAAK,iBAAkBxC,EAAQjB,EAAKU,MAAM8C,eAG5C,MAAa,OAATvC,GAAiBA,GAAS,EACrBA,EAGsBb,SAA3BJ,EAAKU,MAAMgD,aAA6B1D,EAAKU,MAAMgD,aAAe,EAC7D1D,EAAKU,MAAMgD,YAGb,EAGT,QAASG,GAA2B7D,EAAMkC,GACxC,MAAOS,GAAkB3C,EAAMkC,GAAQkB,EAAiBpD,EAAMkC,GAGhE,QAAS4B,GAA4B9D,EAAMkC,GACzC,MAAOgB,GAAmBlD,EAAMkC,GAAQyB,EAAkB3D,EAAMkC,GAGlE,QAAS6B,GAAc/D,EAAMkC,GAC3B,MAAOD,GAAiBjC,EAAMkC,GAAQO,EAAkBzC,EAAMkC,GAGhE,QAAS8B,GAAwBhE,EAAMkC,GACrC,MAAO2B,GAA2B7D,EAAMkC,GACpC4B,EAA4B9D,EAAMkC,GAGxC,QAAS+B,GAAkBjE,GACzB,MAAIA,GAAKU,MAAMwD,eACNlE,EAAKU,MAAMwD,eAEb,aAGT,QAASC,GAAgBnE,GACvB,MAAIA,GAAKU,MAAM0D,aACNpE,EAAKU,MAAM0D,aAEb,aAGT,QAASC,GAAarE,EAAMsE,GAC1B,MAAIA,GAAM5D,MAAM6D,UACPD,EAAM5D,MAAM6D,UAEjBvE,EAAKU,MAAM8D,WACNxE,EAAKU,MAAM8D,WAEb,UAGT,QAASC,GAAYvC,EAAMwC,GACzB,GAAIA,IAAcC,GAAmB,CACnC,GAAIzC,IAASZ,GACX,MAAOC,GACF,IAAIW,IAASX,GAClB,MAAOD,IAIX,MAAOY,GAGT,QAAS0C,GAAiB5E,EAAM6E,GAC9B,GAAIH,EAWJ,OATEA,GADE1E,EAAKU,MAAMgE,UACD1E,EAAKU,MAAMgE,UAEXI,EAGVJ,IAAcI,IAChBJ,EAAiCtE,SAApByE,EAAgCE,GAAoBF,GAG5DH,EAGT,QAASM,GAAiBhF,GACxB,MAAIA,GAAKU,MAAMW,cACNrB,EAAKU,MAAMW,cAEbI,GAGT,QAASwD,GAAsB5D,EAAeqD,GAC5C,MAAIlD,GAAkBH,GACboD,EAAYnD,GAAwBoD,GAEpCjD,GAIX,QAASyD,GAAgBlF,GACvB,MAAIA,GAAKU,MAAMyE,SACNnF,EAAKU,MAAMyE,SAEbC,GAGT,QAASC,GAAYrF,GACnB,MAAIA,GAAKU,MAAM4E,SACNtF,EAAKU,MAAM4E,SAEbC,GAGT,QAASC,GAAOxF,GACd,MACEkF,GAAgBlF,KAAUoF,IACNhF,SAApBJ,EAAKU,MAAMkB,MAA0C,IAApB5B,EAAKU,MAAMkB,KAIhD,QAAS6D,GAAWzF,GAClB,MAA+B,SAAxBA,EAAKU,MAAMgF,SAGpB,QAASC,GAAiB3F,EAAMkC,GAC9B,MAAOlC,GAAKC,OAAO2F,GAAY1D,IAAS6B,EAAc/D,EAAMkC,GAG9D,QAAS2D,GAAkB7F,EAAMkC,GAC/B,MAAiC9B,UAA1BJ,EAAKU,MAAMoF,GAAI5D,KAAwBlC,EAAKU,MAAMoF,GAAI5D,KAAU,EAGzE,QAAS6D,GAAmB/F,EAAMkC,GAChC,MAA0C9B,UAAnCJ,EAAKC,OAAO2F,GAAY1D,KAAwBlC,EAAKC,OAAO2F,GAAY1D,KAAU,EAG3F,QAAS8D,GAAahG,EAAMiG,GAC1B,MAA2B7F,UAApBJ,EAAKU,MAAMuF,GAGpB,QAASC,GAAiBlG,GACxB,MAA8BI,UAAvBJ,EAAKU,MAAME,QAGpB,QAASuF,GAAYnG,EAAMiG,GACzB,MAAwB7F,UAApBJ,EAAKU,MAAMuF,GACNjG,EAAKU,MAAMuF,GAEb,EAGT,QAASG,GAAyBpG,EAAMkC,EAAMjB,GAC5C,GAAIoF,IACFC,IAAOtG,EAAKU,MAAM6F,SAClBC,cAAexG,EAAKU,MAAM6F,SAC1BE,OAAUzG,EAAKU,MAAMgG,UACrBC,iBAAkB3G,EAAKU,MAAMgG,WAC7BxE,GAEE0E,GACFN,IAAOtG,EAAKU,MAAMmG,SAClBL,cAAexG,EAAKU,MAAMmG,SAC1BJ,OAAUzG,EAAKU,MAAMoG,UACrBH,iBAAkB3G,EAAKU,MAAMoG,WAC7B5E,GAEE6E,EAAa9F,CAOjB,OANYb,UAARwG,GAAqBA,GAAO,GAAKG,EAAaH,IAChDG,EAAaH,GAEHxG,SAARiG,GAAqBA,GAAO,GAAkBA,EAAbU,IACnCA,EAAaV,GAERU,EAGT,QAASC,GAAMC,EAAGC,GAChB,MAAQA,GAAJD,EACKA,EAEFC,EAGT,QAASC,GAAMF,EAAGC,GAChB,MAAID,GAAIC,EACCD,EAEFC,EAKT,QAASE,GAAUpH,EAAMkC,EAAMjB,GAC7B,MAAOkG,GAAMf,EAAyBpG,EAAMkC,EAAMjB,GAAQ+C,EAAwBhE,EAAMkC,IAG1F,QAASmF,GAAoBrH,EAAMsE,EAAOpC,GACxC,GAAIoF,GAAQpC,EAAgBZ,KAAWiD,GACrC,EACAjD,EAAMrE,OAAO2F,GAAY1D,GAC3BoC,GAAMrE,OAAOuH,GAAStF,IAASlC,EAAKC,OAAO2F,GAAY1D,IAASoF,EAAOhD,EAAMrE,OAAOgG,GAAI/D,IAK1F,QAASuF,GAAoBzH,EAAMkC,GACjC,MAAkC9B,UAA9BJ,EAAKU,MAAMgH,GAAQxF,IACdiE,EAAYnG,EAAM0H,GAAQxF,KAE3BiE,EAAYnG,EAAMwH,GAAStF,IAGrC,QAASyF,GAAY3H,EAAM0E,GACzB,GAAIkD,GAAWnD,EAAYO,EAAiBhF,GAAO0E,GAC/CmD,EAAY5C,EAAsB2C,EAAUlD,EAEhD1E,GAAKC,OAAOyH,GAAQE,IAAa3F,EAAiBjC,EAAM4H,GACtDH,EAAoBzH,EAAM4H,GAC5B5H,EAAKC,OAAOuH,GAASI,IAAanF,EAAkBzC,EAAM4H,GACxDH,EAAoBzH,EAAM4H,GAC5B5H,EAAKC,OAAOyH,GAAQG,IAAc5F,EAAiBjC,EAAM6H,GACvDJ,EAAoBzH,EAAM6H,GAC5B7H,EAAKC,OAAOuH,GAASK,IAAcpF,EAAkBzC,EAAM6H,GACzDJ,EAAoBzH,EAAM6H,GAG9B,QAASC,GAAOC,EAAWC,GACzB,IAAKD,EACH,KAAM,IAAIjH,OAAMkH,GA+EpB,QAASC,GAAejI,EAAMkI,EAAgBC,EAAoCtD,EAAiBuD,EAAkBC,EAAmBC,GACtIR,EAAO9G,EAAYkH,GAAkBE,IAAqBG,IAA6B,EAAM,uFAC7FT,EAAO9G,EAAYmH,GAAmBE,IAAsBE,IAA6B,EAAM,wFAE/F,IAAaC,GAA0BxE,EAAwBhE,EAAMsB,IACxDmH,EAA6BzE,EAAwBhE,EAAMyB,IAC3DiH,EAAgB3E,EAAc/D,EAAMsB,IACpCqH,EAAmB5E,EAAc/D,EAAMyB,IAG7BiD,GAAYE,EAAiB5E,EAAM6E,EAI1D,IAHA7E,EAAKC,OAAOyE,UAAYA,GAGpBwB,EAAiBlG,GAArB,CACE,GAAa4I,IAAaV,EAAiBQ,EAAgBF,EAC9CK,GAAcV,EAAkBQ,EAAmBF,CAEhE,IAAIL,IAAqBU,IAA4BT,IAAsBS,GAGzE9I,EAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB4G,EAAiBQ,GACrF1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B0G,EAAkBQ,OACrF,IAAkB,GAAdC,GAGT5I,EAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB,GACpEtB,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B,OACnE,CAGL,GAAiBwH,IAAajJ,EAAKU,MAAME,QAGvCgI,GACAR,EACAS,GACAR,EAGFrI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GACzC8G,IAAqBG,IAA8BH,IAAqBc,GACvED,GAAW9I,MAAQqI,EACnBN,EAAiBQ,GACrB1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAC1C4G,IAAsBE,IAA8BF,IAAsBa,GACzED,GAAW5I,OAASoI,EACpBN,EAAkBQ,QAjC1B,CAyCA,GAAWQ,IAAanJ,EAAKW,SAASE,MACtC,IAAmB,IAAfsI,GASF,MARAnJ,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GACzC8G,IAAqBG,IAA8BH,IAAqBc,GACvEV,EACAN,EAAiBQ,QACrB1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAC1C4G,IAAsBE,IAA8BF,IAAsBa,GACzET,EACAN,EAAkBQ,GAMxB,KAAKL,EAAe,CAGlB,GAAIF,IAAqBc,IAA8C,GAAlBhB,GACjDG,IAAsBa,IAA+C,GAAnBf,EAGpD,MAFAnI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB,QACpEtB,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B,GAI1E,IAAI2G,IAAqBc,IAA8C,GAAlBhB,EAGnD,MAFAlI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB,QACpEtB,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2BT,EAAYmH,GAAmB,EAAKA,EAAkBQ,GAIhI,IAAIN,IAAsBa,IAA+C,GAAnBf,EAGpD,MAFAnI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwBN,EAAYkH,GAAkB,EAAKA,EAAiBQ,QACxH1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B,GAK1E,IAAI2G,IAAqBU,IAA4BT,IAAsBS,GAGzE,MAFA9I,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB4G,EAAiBQ,QACrF1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B0G,EAAkBQ,IAM9F,GAyBmBrE,IACR8E,GACEC,GACAC,GACaC,GACAC,GA9BoB5B,GAAWnD,EAAYO,EAAiBhF,GAAO0E,IAC/CmD,GAAY5C,EAAsB2C,GAAUlD,IAC9E+E,GAAgBrI,EAAewG,IACtB1D,GAAiBD,EAAkBjE,GAC5C0J,GAAiBjE,EAAWzF,GAErB2J,GAAqBvJ,OACrBwJ,GAAuBxJ,OAE7ByJ,GAA8BhG,EAA2B7D,EAAM4H,IAC/DkC,GAA+BhG,EAA4B9D,EAAM4H,IACjEmC,GAA+BlG,EAA2B7D,EAAM6H,IAChEmC,GAA2BhG,EAAwBhE,EAAM4H,IACzDqC,GAA4BjG,EAAwBhE,EAAM6H,IAE7CqC,GAAqBT,GAAgBrB,EAAmBC,EACxD8B,GAAsBV,GAAgBpB,EAAoBD,EAGvEgC,GAAsBlC,EAAiBQ,EAAgBF,EACvD6B,GAAuBlC,EAAkBQ,EAAmBF,EAC5D6B,GAAwBb,GAAgBW,GAAsBC,GAC9DE,GAAyBd,GAAgBY,GAAuBD,EAS7E,KAAKhB,GAAI,EAAOD,GAAJC,GAAgBA,KAAK,CAG/B,GAFA9E,GAAQtE,EAAKW,SAASyI,IAElBd,EAAe,CAEjB,GAAuBkC,IAAiB5F,EAAiBN,GAAOI,GAChEiD,GAAYrD,GAAOkG,IAKjBtF,EAAgBZ,MAAWiD,IAIFnH,SAAvBuJ,KACFA,GAAqBrF,IAEMlE,SAAzBwJ,KACFA,GAAqBa,UAAYnG,IAEnCsF,GAAuBtF,GACvBA,GAAMmG,UAAYrK,QAGdqJ,IAAiB5D,EAAkBvB,GAAOhD,IAG5CgD,GAAMrE,OAAOyK,UAAYvD,EAAM7C,GAAM5D,MAAMP,MAAO6D,EAAwBM,GAAOhD,MACvEmI,IAAiB5D,EAAkBvB,GAAO7C,IAGpD6C,GAAMrE,OAAOyK,UAAYvD,EAAM7C,GAAM5D,MAAML,OAAQ2D,EAAwBM,GAAO7C,KACxEI,EAAgByC,KAAWtD,EAAYsJ,KAOjDjB,GAAasB,EACbrB,GAAcqB,EACdpB,GAAwBhB,GACxBiB,GAAyBjB,GAErB1C,EAAkBvB,GAAOhD,MAC3B+H,GAAa/E,GAAM5D,MAAMP,MAAQ4D,EAAcO,GAAOhD,IACtDiI,GAAwBT,IAEtBjD,EAAkBvB,GAAO7C,MAC3B6H,GAAchF,GAAM5D,MAAML,OAAS0D,EAAcO,GAAO7C,IACxD+H,GAAyBV,IAOtBW,KAAiBzI,EAAYqI,KAAgBrI,EAAYoJ,MAC5Df,GAAae,GACbb,GAAwBL,IAKtB7D,EAAYrF,KAAU4K,IACpBnB,IAAiBzI,EAAYsI,MAAiBtI,EAAYqJ,MAC5Df,GAAce,GACdb,GAAyBN,IAK7B2B,EAAmBvG,GAAO+E,GAAYC,GAAa5E,GAAW6E,GAAuBC,IAAwB,EAAO,WAEpHlF,GAAMrE,OAAOyK,UAAYvD,EAAMsC,GAAgBnF,GAAMrE,OAAO8I,cAAgBzE,GAAMrE,OAAO+I,eAAgBhF,EAAwBM,GAAOsD,MAvCxItD,GAAMrE,OAAOyK,UAAYvD,EAAM,EAAGnD,EAAwBM,GAAOsD,KA2DvE,IAZA,GAAWkD,IAAmB,EACnBC,GAAiB,EAGjBC,GAAY,EAGVC,GAAoB,EAGpBC,GAAiB,EAEN/B,GAAjB4B,IAA6B,CAIlC,GAAWI,IAAc,EAMZC,GAA4B,EAE5BC,GAAuB,EACvBC,GAA+B,CAE5ClC,IAAI0B,EAOJ,KAJA,GAAmBS,IAAqBnL,OACrBoL,GAAuBpL,OAG/B+I,GAAJC,IAAgB,CAIrB,GAHA9E,GAAQtE,EAAKW,SAASyI,IACtB9E,GAAMmH,UAAYT,GAEd9F,EAAgBZ,MAAWiD,GAAuB,CACpD,GAAamE,IAAiBpH,GAAMrE,OAAOyK,UAAY3G,EAAcO,GAAOsD,GAI5E,IAAIwD,GAA4BM,GAAiBpB,IAAyBZ,IAAkByB,GAAc,EACxG,KAGFC,KAA6BM,GAC7BP,KAEI3F,EAAOlB,MACT+G,IAAwBtJ,EAAkBuC,IAI1CgH,IAAgCtJ,EAAoBsC,IAASA,GAAMrE,OAAOyK,WAIjDtK,SAAvBmL,KACFA,GAAqBjH,IAEMlE,SAAzBoL,KACFA,GAAqBf,UAAYnG,IAEnCkH,GAAuBlH,GACvBA,GAAMmG,UAAYrK,OAGpBgJ,KACA2B,KAIF,GAAYY,KAAerD,GAAiB6B,KAAwBrB,GAKvD8C,GAAiB,EACjBC,GAAiB,EAMjBC,GAAqB,CAC7B9K,GAAYsJ,IAEsB,EAA5Bc,KAITU,IAAsBV,IALtBU,GAAqBxB,GAAwBc,EAQ/C,IAAaW,IAA6BD,GAC7BE,GAAiB,CAE9B,KAAKL,GAAa,CAChB,GAAaM,IACAC,GACAC,GACAC,GACAC,GAgBAC,GAA+B,EAC/BC,GAAuB,CAEpC,KADAf,GAAuBD,GACSnL,SAAzBoL,IACLS,GAAiBT,GAAqBvL,OAAOyK,UAEpB,EAArBoB,IACFI,GAAyBlK,EAAoBwJ,IAAwBS,GAGtC,IAA3BC,KACFE,GAAeH,GACbH,GAAqBR,GAA+BY,GACtDG,GAAgBjF,EAAUoE,GAAsB5D,GAAUwE,IACtDA,KAAiBC,KAInBL,IAAkBK,GAAgBJ,GAClCK,IAAgCJ,MAG3BJ,GAAqB,IAC9BK,GAAiBpK,EAAkByJ,IAGZ,IAAnBW,KACFC,GAAeH,GACbH,GAAqBT,GAAuBc,GAC9CE,GAAgBjF,EAAUoE,GAAsB5D,GAAUwE,IACtDA,KAAiBC,KAInBL,IAAkBK,GAAgBJ,GAClCM,IAAwBJ,MAK9BX,GAAuBA,GAAqBf,SAU9C,KAPAa,IAAgCgB,GAChCjB,IAAwBkB,GACxBT,IAAsBE,GAGtBA,GAAiB,EACjBR,GAAuBD,GACSnL,SAAzBoL,IAAoC,CACzCS,GAAiBT,GAAqBvL,OAAOyK,SAC7C,IAAa8B,IAAkBP,EAEN,GAArBH,IACFI,GAAyBlK,EAAoBwJ,IAAwBS,GAGtC,IAA3BC,KACFM,GAAkBpF,EAAUoE,GAAsB5D,GAAUqE,GAC1DH,GAAqBR,GAA+BY,MAE/CJ,GAAqB,IAC9BK,GAAiBpK,EAAkByJ,IAGZ,IAAnBW,KACFK,GAAkBpF,EAAUoE,GAAsB5D,GAAUqE,GAC1DH,GAAqBT,GAAuBc,MAIlDH,IAAkBQ,GAAkBP,GAEhCxC,IACFJ,GAAamD,GAAkBzI,EAAcyH,GAAsBlK,IACnEiI,GAAwBT,GAEnBjD,EAAkB2F,GAAsB/J,KAI3C6H,GAAckC,GAAqB9K,MAAML,OAAS0D,EAAcyH,GAAsB/J,IACtF+H,GAAyBV,KAJzBQ,GAAciB,GACdf,GAAyBxI,EAAYsI,IAAef,GAA6BW,MAMnFI,GAAckD,GAAkBzI,EAAcyH,GAAsB/J,IACpE+H,GAAyBV,GAEpBjD,EAAkB2F,GAAsBlK,KAI3C+H,GAAamC,GAAqB9K,MAAMP,MAAQ4D,EAAcyH,GAAsBlK,IACpFiI,GAAwBT,KAJxBO,GAAakB,GACbhB,GAAwBvI,EAAYqI,IAAcd,GAA6BW,IAOnF,IAAYuD,KAAyB5G,EAAkB2F,GAAsB3D,KAC3ExD,EAAarE,EAAMwL,MAA0BkB,EAG/C7B,GAAmBW,GAAsBnC,GAAYC,GAAa5E,GAAW6E,GAAuBC,GAAwBlB,IAAkBmE,GAAuB,QAErKjB,GAAuBA,GAAqBf,WAIhDqB,GAAqBC,GAA6BC,GAW9C9B,KAAuBhB,KACzB4C,GAAqB,GAKnB5H,KAAmByI,KACjBzI,KAAmB0I,GACrBhB,GAAiBE,GAAqB,EAC7B5H,KAAmB2I,GAC5BjB,GAAiBE,GACR5H,KAAmB4I,IAC5BhB,GAAqB3E,EAAM2E,GAAoB,GAE7CD,GADEV,GAAc,EACCW,IAAsBX,GAAc,GAEpC,GAEVjH,KAAmB6I,KAE5BlB,GAAiBC,GAAqBX,GACtCS,GAAiBC,GAAiB,GAItC,IAAamB,IAAUnD,GAA8B+B,GACxCqB,GAAW,CAExB,KAAK7D,GAAI0B,GAAsBC,GAAJ3B,KAAsBA,GAC/C9E,GAAQtE,EAAKW,SAASyI,IAElBlE,EAAgBZ,MAAWiD,IAC3BvB,EAAa1B,GAAOoD,GAAQE,KAC1BU,IAIFhE,GAAMrE,OAAOgG,GAAI2B,KAAazB,EAAY7B,GAAOoD,GAAQE,KACvDxE,EAAiBpD,EAAM4H,IACvB3F,EAAiBqC,GAAOsD,MAGxBU,IAGFhE,GAAMrE,OAAOgG,GAAI2B,MAAcoF,IAM7B9H,EAAgBZ,MAAWc,KACzBuG,IAGFqB,IAAWnB,GAAiB9H,EAAcO,GAAOsD,IAAYtD,GAAMrE,OAAOyK,UAC1EuC,GAAW1C,KAIXyC,IAAWnB,GAAiBlG,EAAiBrB,GAAOsD,IAIpDqF,GAAW9F,EAAM8F,GAAUtH,EAAiBrB,GAAOuD,OAM3DmF,KAAWlD,EAEX,IAAaoD,IAAqB3C,EAoBlC,IAnBIJ,KAAwB5B,IAA8B4B,KAAwBjB,KAEhFgE,GAAqB9F,EAAUpH,EAAM6H,GAAWoF,GAAWhD,IAA6BA,GAEpFE,KAAwBjB,KAC1BgE,GAAqBlG,EAAMkG,GAAoB3C,MAK9Cb,IAAkBS,KAAwBrB,KAC7CmE,GAAW1C,IAIb0C,GAAW7F,EAAUpH,EAAM6H,GAAWoF,GAAWhD,IAA6BA,GAI1E3B,EACF,IAAKc,GAAI0B,GAAsBC,GAAJ3B,KAAsBA,GAG/C,GAFA9E,GAAQtE,EAAKW,SAASyI,IAElBlE,EAAgBZ,MAAWiD,GAGzBvB,EAAa1B,GAAOoD,GAAQG,KAC9BvD,GAAMrE,OAAOgG,GAAI4B,KAAc1B,EAAY7B,GAAOoD,GAAQG,KACxDzE,EAAiBpD,EAAM6H,IACvB5F,EAAiBqC,GAAOuD,IAE1BvD,GAAMrE,OAAOgG,GAAI4B,KAAckC,GAC7B9H,EAAiBqC,GAAOuD,QAEvB,CACL,GAAasF,IAAkBpD,GAIZqD,GAAY/I,EAAarE,EAAMsE,GAIlD,IAAI8I,KAAcV,GAAmB,CACnCrD,GAAa/E,GAAMrE,OAAO8I,cAAgBhF,EAAcO,GAAOhD,IAC/DgI,GAAchF,GAAMrE,OAAO+I,eAAiBjF,EAAcO,GAAO7C,GACjE,IAAY4L,KAAsB,CAE9B5D,KACF4D,GAAsBxH,EAAkBvB,GAAO7C,IAC/C6H,GAAc2D,KAEdI,GAAsBxH,EAAkBvB,GAAOhD,IAC/C+H,GAAa4D,IAIVI,KACH9D,GAAwBvI,EAAYqI,IAAcd,GAA6BO,GAC/EU,GAAyBxI,EAAYsI,IAAef,GAA6BO,GACjF+B,EAAmBvG,GAAO+E,GAAYC,GAAa5E,GAAW6E,GAAuBC,IAAwB,EAAM,gBAEhH,IAAI4D,KAAcE,GAAsB,CAC7C,GAAaC,IAAoBL,GAAqBvH,EAAiBrB,GAAOuD,GAG5EsF,KADEC,KAAcI,GACGD,GAAoB,EAEpBA,GAKvBjJ,GAAMrE,OAAOgG,GAAI4B,MAAeoD,GAAoBkC,GAK1DlC,IAAqBgC,GACrB/B,GAAiB/D,EAAM+D,GAAgB8B,IAGvChC,KACAF,GAAmBC,GACnBA,GAAiBD,GAInB,GAAIE,GAAY,GAAK1C,IAAkBtH,EAAYuJ,IAAyB,CAC1E,GAAakD,IAA2BlD,GAAyBU,GAEpDyC,GAAe,EACfC,GAAc5D,GAER3F,GAAeD,EAAgBnE,EAC9CoE,MAAiBwJ,GACnBD,IAAeF,GACNrJ,KAAiBoJ,GAC1BG,IAAeF,GAA2B,EACjCrJ,KAAiBsI,IACtBnC,GAAyBU,KAC3ByC,GAAgBD,GAA2BzC,GAI/C,IAAW6C,IAAW,CACtB,KAAKzE,GAAI,EAAO4B,GAAJ5B,KAAiBA,GAAG,CAC9B,GACW0E,IADAC,GAAaF,GAIXG,GAAa,CAC1B,KAAKF,GAAIC,GAAgB5E,GAAJ2E,KAAkBA,GAErC,GADAxJ,GAAQtE,EAAKW,SAASmN,IAClB5I,EAAgBZ,MAAWc,GAA/B,CAGA,GAAId,GAAMmH,YAAcrC,GACtB,KAEErD,GAAmBzB,GAAOuD,MAC5BmG,GAAa7G,EAAM6G,GACjB1J,GAAMrE,OAAO2F,GAAYiC,KAAc9D,EAAcO,GAAOuD,MAMlE,GAHAgG,GAAWC,GACXE,IAAcN,GAEVpF,EACF,IAAKwF,GAAIC,GAAgBF,GAAJC,KAAgBA,GAEnC,GADAxJ,GAAQtE,EAAKW,SAASmN,IAClB5I,EAAgBZ,MAAWc,GAA/B,CAIA,GAAmB6I,IAAwB5J,EAAarE,EAAMsE,GAC1D2J,MAA0BX,GAC5BhJ,GAAMrE,OAAOgG,GAAI4B,KAAc8F,GAAc1L,EAAiBqC,GAAOuD,IAC5DoG,KAA0BL,GACnCtJ,GAAMrE,OAAOgG,GAAI4B,KAAc8F,GAAcK,GAAavL,EAAkB6B,GAAOuD,IAAavD,GAAMrE,OAAO2F,GAAYiC,KAChHoG,KAA0BT,IACnClE,GAAchF,GAAMrE,OAAO2F,GAAYiC,KACvCvD,GAAMrE,OAAOgG,GAAI4B,KAAc8F,IAAeK,GAAa1E,IAAe,GACjE2E,KAA0BvB,KACnCpI,GAAMrE,OAAOgG,GAAI4B,KAAc8F,GAAc1L,EAAiBqC,GAAOuD,KAO3E8F,IAAeK,IAiCnB,GA5BAhO,EAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB4G,EAAiBQ,GACrF1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B0G,EAAkBQ,GAItFuB,KAAuB3B,GAGzBvI,EAAKC,OAAO2F,GAAYgC,KAAaR,EAAUpH,EAAM4H,GAAUsD,IACtDhB,KAAuBhB,KAChClJ,EAAKC,OAAO2F,GAAYgC,KAAaT,EACnCH,EAAMsD,GAAwBN,GAC5B5D,EAAyBpG,EAAM4H,GAAUsD,KAC3ClB,KAGAG,KAAwB5B,GAG1BvI,EAAKC,OAAO2F,GAAYiC,KAAcT,EAAUpH,EAAM6H,GAAWoD,GAAoBhB,IAC5EE,KAAwBjB,KACjClJ,EAAKC,OAAO2F,GAAYiC,KAAcV,EACpCH,EAAMuD,GAAyBN,GAC7B7D,EAAyBpG,EAAM6H,GAAWoD,GAAoBhB,KAChEA,KAIA3B,EAAe,CACjB,GAAY4F,KAAuB,EACvBC,IAAwB,CAapC,IAXIvG,KAAarG,IACbqG,KAAalG,KACfwM,IAAuB,GAGrBrG,KAActG,IACdsG,KAAcnG,KAChByM,IAAwB,GAItBD,IAAwBC,GAC1B,IAAK/E,GAAI,EAAOD,GAAJC,KAAkBA,GAC5B9E,GAAQtE,EAAKW,SAASyI,IAElB8E,IACF7G,EAAoBrH,EAAMsE,GAAOsD,IAG/BuG,IACF9G,EAAoBrH,EAAMsE,GAAOuD,IAQzC,IADA+B,GAAuBD,GACSvJ,SAAzBwJ,IAGDtB,IAEFe,GAAasB,EACbrB,GAAcqB,EAEV9E,EAAkB+D,GAAsBtI,IAC1C+H,GAAaO,GAAqBlJ,MAAMP,MAAQ4D,EAAc6F,GAAsBtI,IAGhF0E,EAAa4D,GAAsBwE,IAAapI,EAAa4D,GAAsByE,KACrFhF,GAAarJ,EAAKC,OAAO8I,eACtB3F,EAAiBpD,EAAMsB,IAA0BqC,EAAkB3D,EAAMsB,MACzEsI,GAAqBlJ,MAAM0N,GAAYxE,GAAqBlJ,MAAM2N,IACrEhF,GAAajC,EAAUwC,GAAsBtI,GAAwB+H,KAIrExD,EAAkB+D,GAAsBnI,IAC1C6H,GAAcM,GAAqBlJ,MAAML,OAAS0D,EAAc6F,GAAsBnI,IAGlFuE,EAAa4D,GAAsB0E,IAAYtI,EAAa4D,GAAsB2E,KACpFjF,GAActJ,EAAKC,OAAO+I,gBACvB5F,EAAiBpD,EAAMyB,IAA6BkC,EAAkB3D,EAAMyB,MAC5EmI,GAAqBlJ,MAAM4N,GAAW1E,GAAqBlJ,MAAM6N,IACpEjF,GAAclC,EAAUwC,GAAsBnI,GAA2B6H,MAKzEtI,EAAYqI,KAAerI,EAAYsI,OACzCC,GAAwBvI,EAAYqI,IAAcd,GAA6BO,GAC/EU,GAAyBxI,EAAYsI,IAAef,GAA6BO,GAM5EW,KAAiBzI,EAAYqI,KAAgBrI,EAAYoJ,MAC5Df,GAAae,GACbb,GAAwBL,IAKtB7D,EAAYrF,KAAU4K,IACpBnB,IAAiBzI,EAAYsI,MAAiBtI,EAAYqJ,MAC5Df,GAAce,GACdb,GAAyBN,IAI7B2B,EAAmBjB,GAAsBP,GAAYC,GAAa5E,GAAW6E,GAAuBC,IAAwB,EAAO,eACnIH,GAAaO,GAAqB3J,OAAO8I,cAAgBhF,EAAc6F,GAAsBtI,IAC7FgI,GAAcM,GAAqB3J,OAAO+I,eAAiBjF,EAAc6F,GAAsBnI,KAGjGoJ,EAAmBjB,GAAsBP,GAAYC,GAAa5E,GAAWoE,GAA0BA,IAA0B,EAAM,cAEnI9C,EAAa4D,GAAsBpC,GAASlG,OAC3C0E,EAAa4D,GAAsBlC,GAAQpG,OAC9CsI,GAAqB3J,OAAOyH,GAAQpG,KAClCtB,EAAKC,OAAO2F,GAAYtE,KACxBsI,GAAqB3J,OAAO2F,GAAYtE,KACxC6E,EAAYyD,GAAsBpC,GAASlG,MAG3C0E,EAAa4D,GAAsBpC,GAAS/F,OAC3CuE,EAAa4D,GAAsBlC,GAAQjG,OAC9CmI,GAAqB3J,OAAOyH,GAAQjG,KAClCzB,EAAKC,OAAO2F,GAAYnE,KACxBmI,GAAqB3J,OAAO2F,GAAYnE,KACxC0E,EAAYyD,GAAsBpC,GAAS/F,OAIjDmI,GAAuBA,GAAqBa,WAYhD,QAASI,GAAmB7K,EAAMkI,EAAgBC,EAAiBtD,EAC/DuD,EAAkBC,EAAmBC,EAAekG,GACtD,GAAIvO,GAASD,EAAKC,OAEdwO,EAAmBzO,EAAKE,SAAWD,EAAOyO,kBAAoBC,GAChE1O,EAAO2O,sBAAwB/J,CAE7B4J,KAEgCrO,SAA9BH,EAAO4O,qBACT5O,EAAO4O,uBAEmBzO,SAAxBH,EAAO6O,eACT7O,EAAO6O,aAAa1G,iBAAmBhI,OACvCH,EAAO6O,aAAazG,kBAAoBjI,QAI5C,IAAI2O,EAOJ,IAAIzG,EACErI,EAAO6O,cACP7O,EAAO6O,aAAa5G,iBAAmBA,GACvCjI,EAAO6O,aAAa3G,kBAAoBA,GACxClI,EAAO6O,aAAa1G,mBAAqBA,GACzCnI,EAAO6O,aAAazG,oBAAsBA,IAC5C0G,EAAgB9O,EAAO6O,kBAEpB,IAAI7O,EAAO4O,mBAChB,IAAK,GAAIzF,GAAI,EAAG4F,EAAM/O,EAAO4O,mBAAmBhO,OAAYmO,EAAJ5F,EAASA,IAC/D,GAAInJ,EAAO4O,mBAAmBzF,GAAGlB,iBAAmBA,GAChDjI,EAAO4O,mBAAmBzF,GAAGjB,kBAAoBA,GACjDlI,EAAO4O,mBAAmBzF,GAAGhB,mBAAqBA,GAClDnI,EAAO4O,mBAAmBzF,GAAGf,oBAAsBA,EAAmB,CACxE0G,EAAgB9O,EAAO4O,mBAAmBzF,EAC1C,OAKN,GAAKqF,GAAqCrO,SAAlB2O,GAOtB,GAHA9G,EAAejI,EAAMkI,EAAgBC,EAAiBtD,EAAiBuD,EAAkBC,EAAmBC,GAC5GrI,EAAO2O,oBAAsB/J,EAEPzE,SAAlB2O,EAA6B,CAC/B,GAAIE,EACA3G,IAE0BlI,SAAxBH,EAAO6O,eACT7O,EAAO6O,iBAETG,EAAgBhP,EAAO6O,eAGW1O,SAA9BH,EAAO4O,qBACT5O,EAAO4O,uBAETI,KACAhP,EAAO4O,mBAAmBK,KAAKD,IAGjCA,EAAc/G,eAAiBA,EAC/B+G,EAAc9G,gBAAkBA,EAChC8G,EAAc7G,iBAAmBA,EACjC6G,EAAc5G,kBAAoBA,EAClC4G,EAAcE,cAAgBlP,EAAO8I,cACrCkG,EAAcG,eAAiBnP,EAAO+I,oBA5BxC/I,GAAOoP,aAAeN,EAAcI,cACpClP,EAAOqP,cAAgBP,EAAcK,cAsCvC,OAPI9G,KACFtI,EAAKC,OAAOE,MAAQH,EAAKC,OAAO8I,cAChC/I,EAAKC,OAAOI,OAASL,EAAKC,OAAO+I,eACjC/I,EAAOsP,cAAe,GAGxBtP,EAAOyO,gBAAkBC,EACjBF,GAAqCrO,SAAlB2O,EAG7B,QAASS,GAAWxP,EAAMkI,EAAgBC,EAAiBtD,GAIzD8J,IAII3N,EAAYkH,IAAmBrC,EAAkB7F,EAAMsB,MACzD4G,EAAiBlI,EAAKU,MAAMP,MAAQ4D,EAAc/D,EAAMsB,KAEtDN,EAAYmH,IAAoBtC,EAAkB7F,EAAMyB,MAC1D0G,EAAkBnI,EAAKU,MAAML,OAAS0D,EAAc/D,EAAMyB,IAG5D,IAAI2G,GAAmBpH,EAAYkH,GAAkBK,GAA6BO,GAC9ET,EAAoBrH,EAAYmH,GAAmBI,GAA6BO,EAEhF+B,GAAmB7K,EAAMkI,EAAgBC,EAAiBtD,EAAiBuD,EAAkBC,GAAmB,EAAM,YACxHV,EAAY3H,EAAMA,EAAKC,OAAOyE,WAjgDlC,GAIIiG,GAJA7I,GAAwB,EAExB6M,EAA0B,EAI1BP,EAAW,OACXE,EAAU,MACVD,EAAY,QACZE,EAAa,SAEbzJ,EAAwB,UACxBC,GAAoB,MACpBJ,GAAoB,MAEpBrD,GAAyB,MACzBC,GAAiC,cACjCE,GAA4B,SAC5BC,GAAoC,iBAEpCiL,GAAyB,aACzBC,GAAqB,SACrBC,GAAuB,WACvBC,GAA4B,gBAC5BC,GAA2B,eAE3BO,GAAuB,aACvBE,GAAmB,SACnBI,GAAqB,WACrBlB,GAAoB,UAEpBtH,GAAwB,WACxBmC,GAAwB,WAExBhC,GAAuB,UACvBqF,GAAsB,SAEtBrC,GAA6B,YAC7BO,GAA2B,UAC3BI,GAA2B,UAE3BxB,IACFpB,IAAO,OACPE,cAAe,QACfC,OAAU,MACVE,iBAAkB,UAEhBa,IACFlB,IAAO,QACPE,cAAe,OACfC,OAAU,SACVE,iBAAkB,OAEhBV,IACFK,IAAO,OACPE,cAAe,QACfC,OAAU,MACVE,iBAAkB,UAEhBb,IACFQ,IAAO,QACPE,cAAe,QACfC,OAAU,SACVE,iBAAkB,UAEhBf,IACFU,IAAO,gBACPE,cAAe,gBACfC,OAAU,iBACVE,iBAAkB,iBAg8CpB,QACEsB,eAAgBA,EAChBpI,cAAe2P,EACfzP,UAAWA,KAYb,OALqB,gBAAZJ,WACTC,OAAOD,QAAUE,GAIV,SAASG,GAGdH,EAAcE,UAAUC,GACxBH,EAAcA,cAAcG","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n \n var POSITIVE_FLEX_IS_AUTO = false;\n \n var gCurrentGenerationCount = 0;\n \n var CSS_UNDEFINED;\n \n var CSS_LEFT = 'left';\n var CSS_TOP = 'top';\n var CSS_RIGHT = 'right';\n var CSS_BOTTOM = 'bottom';\n \n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n \n var CSS_OVERFLOW_VISIBLE = 'visible';\n var CSS_OVERFLOW_HIDDEN = 'hidden';\n \n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n var measuredDim = {\n 'row': 'measuredWidth',\n 'row-reverse': 'measuredWidth',\n 'column': 'measuredHeight',\n 'column-reverse': 'measuredHeight'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || Number.isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n \n function getFlex(node) {\n if (node.style.flex === undefined) {\n return 0;\n }\n return node.style.flex;\n }\n \n function isFlexBasisAuto(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // All flex values are auto.\n return true;\n } else {\n // A flex value > 0 implies a basis of zero.\n return getFlex(node) <= 0;\n }\n }\n \n function getFlexGrowFactor(node) {\n // Flex grow is implied by positive values for flex.\n if (getFlex(node) > 0) {\n return getFlex(node);\n }\n return 0;\n }\n \n function getFlexShrinkFactor(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // A flex shrink factor of 1 is implied by non-zero values for flex.\n if (getFlex(node) !== 0) {\n return 1;\n }\n } else {\n // A flex shrink factor of 1 is implied by negative values for flex.\n if (getFlex(node) < 0) {\n return 1;\n }\n }\n return 0;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return CSS_POSITION_RELATIVE;\n }\n \n function getOverflow(node) {\n if (node.style.overflow) {\n return node.style.overflow;\n }\n return CSS_OVERFLOW_VISIBLE;\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex !== undefined && node.style.flex !== 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[measuredDim[axis]] + getMarginAxis(node, axis);\n }\n \n function isStyleDimDefined(node, axis) { \n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n \n function isLayoutDimDefined(node, axis) { \n return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n \n function boundAxisWithinMinAndMax(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n \n function fminf(a, b) {\n if (a < b) {\n return a;\n }\n return b;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n \n // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the\n // padding and border amount.\n function boundAxis(node, axis, value) {\n return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis));\n }\n\n function setTrailingPosition(node, child, axis) {\n var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ?\n 0 :\n child.layout[measuredDim[axis]];\n child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n \n function setPosition(node, direction) {\n var mainAxis = resolveAxis(getFlexDirection(node), direction);\n var crossAxis = getCrossFlexDirection(mainAxis, direction);\n \n node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n }\n \n function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n }\n \n //\n // This is the main routine that implements a subset of the flexbox layout algorithm\n // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.\n //\n // Limitations of this algorithm, compared to the full standard:\n // * Display property is always assumed to be 'flex' except for Text nodes, which\n // are assumed to be 'inline-flex'.\n // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are\n // stacked in document order.\n // * The 'order' property is not supported. The order of flex items is always defined\n // by document order.\n // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'\n // and 'hidden' are not supported.\n // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The\n // rarely-used 'wrap-reverse' is not supported.\n // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and\n // flexBasis, this algorithm supports only the three most common combinations:\n // flex: 0 is equiavlent to flex: 0 0 auto\n // flex: n (where n is a positive value) is equivalent to flex: n 1 auto\n // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0\n // This is faster because the content doesn't need to be measured, but it's\n // less flexible because the basis is always 0 and can't be overriden with\n // the width/height attributes.\n // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto\n // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel\n // values, and the default value is 0.\n // * The 'baseline' value is not supported for alignItems and alignSelf properties.\n // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be\n // specified as pixel values, not as percentages.\n // * There is no support for calculation of dimensions based on intrinsic aspect ratios\n // (e.g. images).\n // * There is no support for forced breaks.\n // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).\n //\n // Deviations from standard:\n // * Section 4.5 of the spec indicates that all flex items have a default minimum\n // main size. For text blocks, for example, this is the width of the widest word. \n // Calculating the minimum width is expensive, so we forego it and assume a default \n // minimum main size of 0.\n // * Min/Max sizes in the main axis are not honored when resolving flexible lengths.\n // * The spec indicates that the default value for 'flexDirection' is 'row', but\n // the algorithm below assumes a default of 'column'.\n //\n // Input parameters:\n // - node: current node to be sized and layed out\n // - availableWidth & availableHeight: available size to be used for sizing the node\n // or CSS_UNDEFINED if the size is not available; interpretation depends on layout\n // flags\n // - parentDirection: the inline (text) direction within the parent (left-to-right or\n // right-to-left)\n // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)\n // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)\n // - performLayout: specifies whether the caller is interested in just the dimensions\n // of the node or it requires the entire node and its subtree to be layed out\n // (with final positions)\n //\n // Details:\n // This routine is called recursively to lay out subtrees of flexbox elements. It uses the\n // information in node.style, which is treated as a read-only input. It is responsible for\n // setting the layout.direction and layout.measured_dimensions fields for the input node as well\n // as the layout.position and layout.line_index fields for its child nodes. The\n // layout.measured_dimensions field includes any border or padding for the node but does\n // not include margins.\n //\n // The spec describes four different layout modes: \"fill available\", \"max content\", \"min content\",\n // and \"fit content\". Of these, we don't use \"min content\" because we don't support default\n // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode\n // from the spec (https://www.w3.org/TR/css3-sizing/#terms):\n // - CSS_MEASURE_MODE_UNDEFINED: max content\n // - CSS_MEASURE_MODE_EXACTLY: fill available\n // - CSS_MEASURE_MODE_AT_MOST: fit content\n // \n // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of\n // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.\n //\n function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) {\n assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n \n var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n // Set the resolved resolution in the node's layout.\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n node.layout.direction = direction;\n\n // For content (text) nodes, determine the dimensions based on the text contents.\n if (isMeasureDefined(node)) {\n var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n \n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n\n // Don't bother sizing the text if both dimensions are already defined.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n } else if (innerWidth <= 0) {\n\n // Don't bother sizing the text if there's no horizontal space.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n } else {\n\n // Measure the text under the current constraints.\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n innerWidth,\n widthMeasureMode,\n innerHeight,\n heightMeasureMode\n );\n\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.width + paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.height + paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n }\n \n return;\n }\n\n // For nodes with no children, use the available values if they were provided, or\n // the minimum size as indicated by the padding and border sizes.\n var/*int*/ childCount = node.children.length;\n if (childCount === 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n return;\n }\n\n // If we're not being asked to perform a full layout, we can handle a number of common\n // cases here without incurring the cost of the remaining function.\n if (!performLayout) {\n // If we're being asked to size the content with an at most constraint but there is no available width,\n // the measurement will always be zero.\n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 &&\n heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn));\n return;\n }\n\n if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow));\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n // If we're being asked to use an exact width/height, there's no need to measure the children.\n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n return;\n }\n }\n\n // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*bool*/ isMainAxisRow = isRowDirection(mainAxis);\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_node_t**/ firstAbsoluteChild = undefined;\n var/*css_node_t**/ currentAbsoluteChild = undefined;\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n \n var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;\n var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;\n\n // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS\n var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;\n var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;\n\n // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM\n var/*css_node_t**/ child;\n var/*int*/ i;\n var/*float*/ childWidth;\n var/*float*/ childHeight;\n var/*css_measure_mode_t*/ childWidthMeasureMode;\n var/*css_measure_mode_t*/ childHeightMeasureMode;\n for (i = 0; i < childCount; i++) {\n child = node.children[i];\n\n if (performLayout) {\n // Set the initial position (relative to the parent).\n var/*css_direction_t*/ childDirection = resolveDirection(child, direction);\n setPosition(child, childDirection);\n }\n \n // Absolute-positioned children don't participate in flex layout. Add them\n // to a list that we can process later.\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === undefined) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== undefined) {\n currentAbsoluteChild.nextChild = child;\n }\n currentAbsoluteChild = child;\n child.nextChild = undefined;\n } else {\n \n if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n \n // The width is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW));\n } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n \n // The height is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN));\n } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) {\n \n // If the basis isn't 'auto', it is assumed to be zero.\n child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));\n } else {\n \n // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n \n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n // Measure the child\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure');\n \n child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis));\n }\n }\n }\n\n // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES\n \n // Indexes of children that represent the first and last items in the line.\n var/*int*/ startOfLineIndex = 0;\n var/*int*/ endOfLineIndex = 0;\n \n // Number of lines.\n var/*int*/ lineCount = 0;\n \n // Accumulated cross dimensions of all lines so far.\n var/*float*/ totalLineCrossDim = 0;\n\n // Max main dimension of all the lines.\n var/*float*/ maxLineMainDim = 0;\n\n while (endOfLineIndex < childCount) {\n \n // Number of items on the currently line. May be different than the difference\n // between start and end indicates because we skip over absolute-positioned items.\n var/*int*/ itemsOnLine = 0;\n\n // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin\n // of all the children on the current line. This will be used in order to\n // either set the dimensions of the node if none already exist or to compute\n // the remaining space left for the flexible children.\n var/*float*/ sizeConsumedOnCurrentLine = 0;\n\n var/*float*/ totalFlexGrowFactors = 0;\n var/*float*/ totalFlexShrinkScaledFactors = 0;\n\n i = startOfLineIndex;\n\n // Maintain a linked list of the child nodes that can shrink and/or grow.\n var/*css_node_t**/ firstRelativeChild = undefined;\n var/*css_node_t**/ currentRelativeChild = undefined;\n\n // Add items to the current line until it's full or we run out of items.\n while (i < childCount) {\n child = node.children[i];\n child.lineIndex = lineCount;\n\n if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) {\n var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis);\n \n // If this is a multi-line flow and this item pushes us over the available size, we've\n // hit the end of the current line. Break out of the loop and lay out the current line.\n if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {\n break;\n }\n\n sizeConsumedOnCurrentLine += outerFlexBasis;\n itemsOnLine++;\n\n if (isFlex(child)) {\n totalFlexGrowFactors += getFlexGrowFactor(child);\n \n // Unlike the grow factor, the shrink factor is scaled relative to the child\n // dimension.\n totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;\n }\n\n // Store a private linked list of children that need to be layed out.\n if (firstRelativeChild === undefined) {\n firstRelativeChild = child;\n }\n if (currentRelativeChild !== undefined) {\n currentRelativeChild.nextChild = child;\n }\n currentRelativeChild = child;\n child.nextChild = undefined;\n }\n \n i++;\n endOfLineIndex++;\n }\n \n // If we don't need to measure the cross axis, we can skip the entire flex step.\n var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY;\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS\n // Calculate the remaining available space that needs to be allocated.\n // If the main dimension size isn't known, it is computed based on\n // the line length, so there's no more space left to distribute.\n var/*float*/ remainingFreeSpace = 0;\n if (!isUndefined(availableInnerMainDim)) {\n remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;\n } else if (sizeConsumedOnCurrentLine < 0) {\n // availableInnerMainDim is indefinite which means the node is being sized based on its content.\n // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for\n // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.\n remainingFreeSpace = -sizeConsumedOnCurrentLine;\n }\n \n var/*float*/ originalRemainingFreeSpace = remainingFreeSpace;\n var/*float*/ deltaFreeSpace = 0;\n\n if (!canSkipFlex) {\n var/*float*/ childFlexBasis;\n var/*float*/ flexShrinkScaledFactor;\n var/*float*/ flexGrowFactor;\n var/*float*/ baseMainSize;\n var/*float*/ boundMainSize;\n \n // Do two passes over the flex items to figure out how to distribute the remaining space.\n // The first pass finds the items whose min/max constraints trigger, freezes them at those\n // sizes, and excludes those sizes from the remaining space. The second pass sets the size\n // of each flexible item. It distributes the remaining space amongst the items whose min/max\n // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing\n // their min/max constraints to trigger again. \n //\n // This two pass approach for resolving min/max constraints deviates from the spec. The\n // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process\n // that needs to be repeated a variable number of times. The algorithm implemented here\n // won't handle all cases but it was simpler to implement and it mitigates performance\n // concerns because we know exactly how many passes it'll do.\n \n // First pass: detect the flex items whose min/max constraints trigger\n var/*float*/ deltaFlexShrinkScaledFactors = 0;\n var/*float*/ deltaFlexGrowFactors = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;\n }\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexGrowFactors -= flexGrowFactor;\n }\n }\n }\n \n currentRelativeChild = currentRelativeChild.nextChild;\n }\n \n totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;\n totalFlexGrowFactors += deltaFlexGrowFactors;\n remainingFreeSpace += deltaFreeSpace;\n \n // Second pass: resolve the sizes of the flexible items\n deltaFreeSpace = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n var/*float*/ updatedMainSize = childFlexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);\n }\n }\n \n deltaFreeSpace -= updatedMainSize - childFlexBasis;\n \n if (isMainAxisRow) {\n childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = availableInnerCrossDim;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n } else {\n childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = availableInnerCrossDim;\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n }\n \n var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) &&\n getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH;\n\n // Recursively call the layout algorithm for this child with the updated main size.\n layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex');\n\n currentRelativeChild = currentRelativeChild.nextChild;\n }\n }\n \n remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace;\n\n // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION\n\n // At this point, all the children have their dimensions set in the main axis.\n // Their dimensions are also set in the cross axis with the exception of items\n // that are aligned 'stretch'. We need to compute these stretch values and\n // set the final positions.\n\n // If we are using \"at most\" rules in the main axis, we won't distribute\n // any remaining space at this point.\n if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n remainingFreeSpace = 0;\n }\n\n // Use justifyContent to figure out how to allocate the remaining space\n // available in the main axis.\n if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingFreeSpace / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingFreeSpace;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingFreeSpace = fmaxf(remainingFreeSpace, 0);\n if (itemsOnLine > 1) {\n betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingFreeSpace / itemsOnLine;\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim;\n var/*float*/ crossDim = 0;\n\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n if (performLayout) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n }\n } else {\n if (performLayout) {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n }\n \n // Now that we placed the element, we need to update the variables.\n // We need to do that only for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n if (canSkipFlex) {\n // If we skipped the flex step, then we can't rely on the measuredDims because\n // they weren't computed. This means we can't call getDimWithMargin.\n mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis;\n crossDim = availableInnerCrossDim;\n } else {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n \n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n }\n }\n }\n }\n\n mainDim += trailingPaddingAndBorderMain;\n \n var/*float*/ containerCrossAxis = availableInnerCrossDim;\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n // Compute the cross axis from the max cross dimension of the children.\n containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n \n if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim);\n }\n }\n\n // If there's no flex wrap, the cross dimension is defined by the container.\n if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) {\n crossDim = availableInnerCrossDim;\n }\n\n // Clamp to the min/max size specified on the container.\n crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n\n // STEP 7: CROSS-AXIS ALIGNMENT\n // We can skip child alignment if we're just measuring the container.\n if (performLayout) {\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // If the child is absolutely positioned and has a top/left/bottom/right\n // set, override all the previously computed positions to set it correctly.\n if (isPosDefined(child, leading[crossAxis])) {\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n } else {\n child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross +\n getLeadingMargin(child, crossAxis);\n }\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n \n // If the child uses align stretch, we need to lay it out one more time, this time\n // forcing the cross-axis size to be the computed cross size for the current line.\n if (alignItem === CSS_ALIGN_STRETCH) {\n childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n var/*bool*/ isCrossSizeDefinite = false;\n \n if (isMainAxisRow) {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeight = crossDim;\n } else {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW);\n childWidth = crossDim;\n }\n \n // If the child defines a definite size for its cross axis, there's no need to stretch.\n if (!isCrossSizeDefinite) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch');\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;\n }\n }\n }\n\n totalLineCrossDim += crossDim;\n maxLineMainDim = fmaxf(maxLineMainDim, mainDim);\n\n // Reset variables for new line.\n lineCount++;\n startOfLineIndex = endOfLineIndex;\n endOfLineIndex = startOfLineIndex;\n }\n\n // STEP 8: MULTI-LINE CONTENT ALIGNMENT\n if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) {\n var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (availableInnerCrossDim > totalLineCrossDim) {\n crossDimLead = (remainingAlignContentDim / lineCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < lineCount; ++i) {\n var/*int*/ startIndex = endIndex;\n var/*int*/ j;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (j = startIndex; j < childCount; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(lineHeight,\n child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis));\n }\n }\n endIndex = j;\n lineHeight += crossDimLead;\n\n if (performLayout) {\n for (j = startIndex; j < endIndex; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n childHeight = child.layout[measuredDim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with indefinite\n // (auto) crossAxis dimension.\n }\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n // STEP 9: COMPUTING FINAL DIMENSIONS\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n\n // If the user didn't specify a width or height for the node, set the\n // dimensions based on the children.\n if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);\n } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[mainAxis]] = fmaxf(\n fminf(availableInnerMainDim + paddingAndBorderAxisMain,\n boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),\n paddingAndBorderAxisMain);\n }\n\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);\n } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[crossAxis]] = fmaxf(\n fminf(availableInnerCrossDim + paddingAndBorderAxisCross,\n boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),\n paddingAndBorderAxisCross);\n }\n \n // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN\n if (performLayout) {\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n\n // Set trailing position if necessary.\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n }\n \n // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== undefined) {\n // Now that we know the bounds of the container, perform layout again on the\n // absolutely-positioned children.\n if (performLayout) {\n\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n\n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n } else {\n // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) {\n childWidth = node.layout.measuredWidth -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) -\n (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]);\n childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);\n }\n }\n \n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n } else {\n // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) {\n childHeight = node.layout.measuredHeight -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) -\n (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]);\n childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);\n }\n }\n\n // If we're still missing one or the other dimension, measure the content.\n if (isUndefined(childWidth) || isUndefined(childHeight)) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure');\n childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout');\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]);\n }\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]);\n }\n }\n\n currentAbsoluteChild = currentAbsoluteChild.nextChild;\n }\n }\n \n //\n // This is a wrapper around the layoutNodeImpl function. It determines\n // whether the layout request is redundant and can be skipped.\n //\n // Parameters:\n // Input parameters are the same as layoutNodeImpl (see above)\n // Return parameter is true if layout was performed, false if skipped\n //\n function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection,\n widthMeasureMode, heightMeasureMode, performLayout, reason) {\n var layout = node.layout;\n\n var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) ||\n layout.lastParentDirection !== parentDirection;\n\n if (needToVisitNode) {\n // Invalidate the cached results.\n if (layout.cachedMeasurements !== undefined) {\n layout.cachedMeasurements = []; \n }\n if (layout.cachedLayout !== undefined) {\n layout.cachedLayout.widthMeasureMode = undefined;\n layout.cachedLayout.heightMeasureMode = undefined;\n }\n }\n\n var cachedResults;\n \n // Determine whether the results are already cached. We maintain a separate\n // cache for layouts and measurements. A layout operation modifies the positions\n // and dimensions for nodes in the subtree. The algorithm assumes that each node\n // gets layed out a maximum of one time per tree layout, but multiple measurements\n // may be required to resolve all of the flex dimensions.\n if (performLayout) {\n if (layout.cachedLayout &&\n layout.cachedLayout.availableWidth === availableWidth &&\n layout.cachedLayout.availableHeight === availableHeight &&\n layout.cachedLayout.widthMeasureMode === widthMeasureMode &&\n layout.cachedLayout.heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedLayout;\n }\n } else if (layout.cachedMeasurements) {\n for (var i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (layout.cachedMeasurements[i].availableWidth === availableWidth &&\n layout.cachedMeasurements[i].availableHeight === availableHeight &&\n layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode &&\n layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n \n if (!needToVisitNode && cachedResults !== undefined) {\n layout.measureWidth = cachedResults.computedWidth;\n layout.measureHeight = cachedResults.computedHeight;\n } else {\n layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);\n layout.lastParentDirection = parentDirection;\n \n if (cachedResults === undefined) {\n var newCacheEntry;\n if (performLayout) {\n // Use the single layout cache entry.\n if (layout.cachedLayout === undefined) {\n layout.cachedLayout = {};\n }\n newCacheEntry = layout.cachedLayout;\n } else {\n // Allocate a new measurement cache entry.\n if (layout.cachedMeasurements === undefined) {\n layout.cachedMeasurements = [];\n }\n newCacheEntry = {};\n layout.cachedMeasurements.push(newCacheEntry);\n }\n \n newCacheEntry.availableWidth = availableWidth;\n newCacheEntry.availableHeight = availableHeight;\n newCacheEntry.widthMeasureMode = widthMeasureMode;\n newCacheEntry.heightMeasureMode = heightMeasureMode;\n newCacheEntry.computedWidth = layout.measuredWidth;\n newCacheEntry.computedHeight = layout.measuredHeight;\n }\n }\n \n if (performLayout) {\n node.layout.width = node.layout.measuredWidth;\n node.layout.height = node.layout.measuredHeight;\n layout.shouldUpdate = true;\n }\n \n layout.generationCount = gCurrentGenerationCount;\n return (needToVisitNode || cachedResults === undefined);\n }\n \n function layoutNode(node, availableWidth, availableHeight, parentDirection) {\n // Increment the generation count. This will force the recursive routine to visit\n // all dirty nodes at least once. Subsequent visits will be skipped if the input\n // parameters don't change.\n gCurrentGenerationCount++;\n \n // If the caller didn't specify a height/width, use the dimensions\n // specified in the style.\n if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n }\n if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) {\n setPosition(node, node.layout.direction);\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file +{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","Number","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getFlex","flex","isFlexBasisAuto","POSITIVE_FLEX_IS_AUTO","getFlexGrowFactor","getFlexShrinkFactor","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","CSS_POSITION_RELATIVE","getOverflow","overflow","CSS_OVERFLOW_VISIBLE","isFlex","isFlexWrap","flexWrap","getDimWithMargin","measuredDim","isStyleDimDefined","dim","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxisWithinMinAndMax","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fminf","a","b","fmaxf","boundAxis","setTrailingPosition","size","CSS_POSITION_ABSOLUTE","trailing","getRelativePosition","leading","setPosition","mainAxis","crossAxis","assert","condition","message","layoutNodeImpl","availableWidth","availableHeight","widthMeasureMode","heightMeasureMode","performLayout","CSS_MEASURE_MODE_UNDEFINED","paddingAndBorderAxisRow","paddingAndBorderAxisColumn","marginAxisRow","marginAxisColumn","innerWidth","innerHeight","CSS_MEASURE_MODE_EXACTLY","measuredWidth","measuredHeight","measureDim","CSS_MEASURE_MODE_AT_MOST","childCount","i","childWidth","childHeight","childWidthMeasureMode","childHeightMeasureMode","isMainAxisRow","isNodeFlexWrap","firstAbsoluteChild","currentAbsoluteChild","leadingPaddingAndBorderMain","trailingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","measureModeMainDim","measureModeCrossDim","availableInnerWidth","availableInnerHeight","availableInnerMainDim","availableInnerCrossDim","childDirection","nextChild","flexBasis","CSS_UNDEFINED","CSS_OVERFLOW_HIDDEN","layoutNodeInternal","startOfLineIndex","endOfLineIndex","lineCount","totalLineCrossDim","maxLineMainDim","itemsOnLine","sizeConsumedOnCurrentLine","totalFlexGrowFactors","totalFlexShrinkScaledFactors","firstRelativeChild","currentRelativeChild","lineIndex","outerFlexBasis","canSkipFlex","leadingMainDim","betweenMainDim","remainingFreeSpace","originalRemainingFreeSpace","deltaFreeSpace","childFlexBasis","flexShrinkScaledFactor","flexGrowFactor","baseMainSize","boundMainSize","deltaFlexShrinkScaledFactors","deltaFlexGrowFactors","updatedMainSize","requiresStretchLayout","CSS_ALIGN_STRETCH","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","mainDim","crossDim","containerCrossAxis","leadingCrossDim","alignItem","isCrossSizeDefinite","CSS_ALIGN_FLEX_START","remainingCrossDim","CSS_ALIGN_CENTER","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","j","startIndex","lineHeight","alignContentAlignItem","needsMainTrailingPos","needsCrossTrailingPos","CSS_LEFT","CSS_RIGHT","CSS_TOP","CSS_BOTTOM","canUseCachedMeasurement","marginRow","marginColumn","cachedLayout","computedHeight","computedWidth","reason","needToVisitNode","generationCount","gCurrentGenerationCount","lastParentDirection","cachedMeasurements","len","cachedResults","newCacheEntry","push","measureWidth","measureHeight","shouldUpdate","layoutNode"],"mappings":"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","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n \n var POSITIVE_FLEX_IS_AUTO = false;\n \n var gCurrentGenerationCount = 0;\n \n var CSS_UNDEFINED;\n \n var CSS_LEFT = 'left';\n var CSS_TOP = 'top';\n var CSS_RIGHT = 'right';\n var CSS_BOTTOM = 'bottom';\n \n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n \n var CSS_OVERFLOW_VISIBLE = 'visible';\n var CSS_OVERFLOW_HIDDEN = 'hidden';\n \n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n var measuredDim = {\n 'row': 'measuredWidth',\n 'row-reverse': 'measuredWidth',\n 'column': 'measuredHeight',\n 'column-reverse': 'measuredHeight'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || Number.isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n \n function getFlex(node) {\n if (node.style.flex === undefined) {\n return 0;\n }\n return node.style.flex;\n }\n \n function isFlexBasisAuto(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // All flex values are auto.\n return true;\n } else {\n // A flex value > 0 implies a basis of zero.\n return getFlex(node) <= 0;\n }\n }\n \n function getFlexGrowFactor(node) {\n // Flex grow is implied by positive values for flex.\n if (getFlex(node) > 0) {\n return getFlex(node);\n }\n return 0;\n }\n \n function getFlexShrinkFactor(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // A flex shrink factor of 1 is implied by non-zero values for flex.\n if (getFlex(node) !== 0) {\n return 1;\n }\n } else {\n // A flex shrink factor of 1 is implied by negative values for flex.\n if (getFlex(node) < 0) {\n return 1;\n }\n }\n return 0;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return CSS_POSITION_RELATIVE;\n }\n \n function getOverflow(node) {\n if (node.style.overflow) {\n return node.style.overflow;\n }\n return CSS_OVERFLOW_VISIBLE;\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex !== undefined && node.style.flex !== 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[measuredDim[axis]] + getMarginAxis(node, axis);\n }\n \n function isStyleDimDefined(node, axis) { \n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n \n function isLayoutDimDefined(node, axis) { \n return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n \n function boundAxisWithinMinAndMax(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n \n function fminf(a, b) {\n if (a < b) {\n return a;\n }\n return b;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n \n // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the\n // padding and border amount.\n function boundAxis(node, axis, value) {\n return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis));\n }\n\n function setTrailingPosition(node, child, axis) {\n var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ?\n 0 :\n child.layout[measuredDim[axis]];\n child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n \n function setPosition(node, direction) {\n var mainAxis = resolveAxis(getFlexDirection(node), direction);\n var crossAxis = getCrossFlexDirection(mainAxis, direction);\n \n node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n }\n \n function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n }\n \n //\n // This is the main routine that implements a subset of the flexbox layout algorithm\n // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.\n //\n // Limitations of this algorithm, compared to the full standard:\n // * Display property is always assumed to be 'flex' except for Text nodes, which\n // are assumed to be 'inline-flex'.\n // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are\n // stacked in document order.\n // * The 'order' property is not supported. The order of flex items is always defined\n // by document order.\n // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'\n // and 'hidden' are not supported.\n // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The\n // rarely-used 'wrap-reverse' is not supported.\n // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and\n // flexBasis, this algorithm supports only the three most common combinations:\n // flex: 0 is equiavlent to flex: 0 0 auto\n // flex: n (where n is a positive value) is equivalent to flex: n 1 auto\n // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0\n // This is faster because the content doesn't need to be measured, but it's\n // less flexible because the basis is always 0 and can't be overriden with\n // the width/height attributes.\n // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto\n // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel\n // values, and the default value is 0.\n // * The 'baseline' value is not supported for alignItems and alignSelf properties.\n // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be\n // specified as pixel values, not as percentages.\n // * There is no support for calculation of dimensions based on intrinsic aspect ratios\n // (e.g. images).\n // * There is no support for forced breaks.\n // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).\n //\n // Deviations from standard:\n // * Section 4.5 of the spec indicates that all flex items have a default minimum\n // main size. For text blocks, for example, this is the width of the widest word. \n // Calculating the minimum width is expensive, so we forego it and assume a default \n // minimum main size of 0.\n // * Min/Max sizes in the main axis are not honored when resolving flexible lengths.\n // * The spec indicates that the default value for 'flexDirection' is 'row', but\n // the algorithm below assumes a default of 'column'.\n //\n // Input parameters:\n // - node: current node to be sized and layed out\n // - availableWidth & availableHeight: available size to be used for sizing the node\n // or CSS_UNDEFINED if the size is not available; interpretation depends on layout\n // flags\n // - parentDirection: the inline (text) direction within the parent (left-to-right or\n // right-to-left)\n // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)\n // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)\n // - performLayout: specifies whether the caller is interested in just the dimensions\n // of the node or it requires the entire node and its subtree to be layed out\n // (with final positions)\n //\n // Details:\n // This routine is called recursively to lay out subtrees of flexbox elements. It uses the\n // information in node.style, which is treated as a read-only input. It is responsible for\n // setting the layout.direction and layout.measured_dimensions fields for the input node as well\n // as the layout.position and layout.line_index fields for its child nodes. The\n // layout.measured_dimensions field includes any border or padding for the node but does\n // not include margins.\n //\n // The spec describes four different layout modes: \"fill available\", \"max content\", \"min content\",\n // and \"fit content\". Of these, we don't use \"min content\" because we don't support default\n // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode\n // from the spec (https://www.w3.org/TR/css3-sizing/#terms):\n // - CSS_MEASURE_MODE_UNDEFINED: max content\n // - CSS_MEASURE_MODE_EXACTLY: fill available\n // - CSS_MEASURE_MODE_AT_MOST: fit content\n // \n // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of\n // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.\n //\n function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) {\n assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n \n var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n // Set the resolved resolution in the node's layout.\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n node.layout.direction = direction;\n\n // For content (text) nodes, determine the dimensions based on the text contents.\n if (isMeasureDefined(node)) {\n var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n \n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n\n // Don't bother sizing the text if both dimensions are already defined.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n } else if (innerWidth <= 0) {\n\n // Don't bother sizing the text if there's no horizontal space.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n } else {\n\n // Measure the text under the current constraints.\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n innerWidth,\n widthMeasureMode,\n innerHeight,\n heightMeasureMode\n );\n\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.width + paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.height + paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n }\n \n return;\n }\n\n // For nodes with no children, use the available values if they were provided, or\n // the minimum size as indicated by the padding and border sizes.\n var/*int*/ childCount = node.children.length;\n if (childCount === 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n return;\n }\n\n // If we're not being asked to perform a full layout, we can handle a number of common\n // cases here without incurring the cost of the remaining function.\n if (!performLayout) {\n // If we're being asked to size the content with an at most constraint but there is no available width,\n // the measurement will always be zero.\n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 &&\n heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn));\n return;\n }\n\n if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow));\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n // If we're being asked to use an exact width/height, there's no need to measure the children.\n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n return;\n }\n }\n\n // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*bool*/ isMainAxisRow = isRowDirection(mainAxis);\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_node_t**/ firstAbsoluteChild = undefined;\n var/*css_node_t**/ currentAbsoluteChild = undefined;\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n \n var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;\n var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;\n\n // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS\n var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;\n var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;\n\n // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM\n var/*css_node_t**/ child;\n var/*int*/ i;\n var/*float*/ childWidth;\n var/*float*/ childHeight;\n var/*css_measure_mode_t*/ childWidthMeasureMode;\n var/*css_measure_mode_t*/ childHeightMeasureMode;\n for (i = 0; i < childCount; i++) {\n child = node.children[i];\n\n if (performLayout) {\n // Set the initial position (relative to the parent).\n var/*css_direction_t*/ childDirection = resolveDirection(child, direction);\n setPosition(child, childDirection);\n }\n \n // Absolute-positioned children don't participate in flex layout. Add them\n // to a list that we can process later.\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === undefined) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== undefined) {\n currentAbsoluteChild.nextChild = child;\n }\n currentAbsoluteChild = child;\n child.nextChild = undefined;\n } else {\n \n if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n \n // The width is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW));\n } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n \n // The height is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN));\n } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) {\n \n // If the basis isn't 'auto', it is assumed to be zero.\n child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));\n } else {\n \n // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n \n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n // Measure the child\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure');\n \n child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis));\n }\n }\n }\n\n // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES\n \n // Indexes of children that represent the first and last items in the line.\n var/*int*/ startOfLineIndex = 0;\n var/*int*/ endOfLineIndex = 0;\n \n // Number of lines.\n var/*int*/ lineCount = 0;\n \n // Accumulated cross dimensions of all lines so far.\n var/*float*/ totalLineCrossDim = 0;\n\n // Max main dimension of all the lines.\n var/*float*/ maxLineMainDim = 0;\n\n while (endOfLineIndex < childCount) {\n \n // Number of items on the currently line. May be different than the difference\n // between start and end indicates because we skip over absolute-positioned items.\n var/*int*/ itemsOnLine = 0;\n\n // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin\n // of all the children on the current line. This will be used in order to\n // either set the dimensions of the node if none already exist or to compute\n // the remaining space left for the flexible children.\n var/*float*/ sizeConsumedOnCurrentLine = 0;\n\n var/*float*/ totalFlexGrowFactors = 0;\n var/*float*/ totalFlexShrinkScaledFactors = 0;\n\n i = startOfLineIndex;\n\n // Maintain a linked list of the child nodes that can shrink and/or grow.\n var/*css_node_t**/ firstRelativeChild = undefined;\n var/*css_node_t**/ currentRelativeChild = undefined;\n\n // Add items to the current line until it's full or we run out of items.\n while (i < childCount) {\n child = node.children[i];\n child.lineIndex = lineCount;\n\n if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) {\n var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis);\n \n // If this is a multi-line flow and this item pushes us over the available size, we've\n // hit the end of the current line. Break out of the loop and lay out the current line.\n if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {\n break;\n }\n\n sizeConsumedOnCurrentLine += outerFlexBasis;\n itemsOnLine++;\n\n if (isFlex(child)) {\n totalFlexGrowFactors += getFlexGrowFactor(child);\n \n // Unlike the grow factor, the shrink factor is scaled relative to the child\n // dimension.\n totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;\n }\n\n // Store a private linked list of children that need to be layed out.\n if (firstRelativeChild === undefined) {\n firstRelativeChild = child;\n }\n if (currentRelativeChild !== undefined) {\n currentRelativeChild.nextChild = child;\n }\n currentRelativeChild = child;\n child.nextChild = undefined;\n }\n \n i++;\n endOfLineIndex++;\n }\n \n // If we don't need to measure the cross axis, we can skip the entire flex step.\n var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY;\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS\n // Calculate the remaining available space that needs to be allocated.\n // If the main dimension size isn't known, it is computed based on\n // the line length, so there's no more space left to distribute.\n var/*float*/ remainingFreeSpace = 0;\n if (!isUndefined(availableInnerMainDim)) {\n remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;\n } else if (sizeConsumedOnCurrentLine < 0) {\n // availableInnerMainDim is indefinite which means the node is being sized based on its content.\n // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for\n // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.\n remainingFreeSpace = -sizeConsumedOnCurrentLine;\n }\n \n var/*float*/ originalRemainingFreeSpace = remainingFreeSpace;\n var/*float*/ deltaFreeSpace = 0;\n\n if (!canSkipFlex) {\n var/*float*/ childFlexBasis;\n var/*float*/ flexShrinkScaledFactor;\n var/*float*/ flexGrowFactor;\n var/*float*/ baseMainSize;\n var/*float*/ boundMainSize;\n \n // Do two passes over the flex items to figure out how to distribute the remaining space.\n // The first pass finds the items whose min/max constraints trigger, freezes them at those\n // sizes, and excludes those sizes from the remaining space. The second pass sets the size\n // of each flexible item. It distributes the remaining space amongst the items whose min/max\n // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing\n // their min/max constraints to trigger again. \n //\n // This two pass approach for resolving min/max constraints deviates from the spec. The\n // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process\n // that needs to be repeated a variable number of times. The algorithm implemented here\n // won't handle all cases but it was simpler to implement and it mitigates performance\n // concerns because we know exactly how many passes it'll do.\n \n // First pass: detect the flex items whose min/max constraints trigger\n var/*float*/ deltaFlexShrinkScaledFactors = 0;\n var/*float*/ deltaFlexGrowFactors = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;\n }\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexGrowFactors -= flexGrowFactor;\n }\n }\n }\n \n currentRelativeChild = currentRelativeChild.nextChild;\n }\n \n totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;\n totalFlexGrowFactors += deltaFlexGrowFactors;\n remainingFreeSpace += deltaFreeSpace;\n \n // Second pass: resolve the sizes of the flexible items\n deltaFreeSpace = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n var/*float*/ updatedMainSize = childFlexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);\n }\n }\n \n deltaFreeSpace -= updatedMainSize - childFlexBasis;\n \n if (isMainAxisRow) {\n childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = availableInnerCrossDim;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n } else {\n childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = availableInnerCrossDim;\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n }\n \n var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) &&\n getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH;\n\n // Recursively call the layout algorithm for this child with the updated main size.\n layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex');\n\n currentRelativeChild = currentRelativeChild.nextChild;\n }\n }\n \n remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace;\n\n // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION\n\n // At this point, all the children have their dimensions set in the main axis.\n // Their dimensions are also set in the cross axis with the exception of items\n // that are aligned 'stretch'. We need to compute these stretch values and\n // set the final positions.\n\n // If we are using \"at most\" rules in the main axis, we won't distribute\n // any remaining space at this point.\n if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n remainingFreeSpace = 0;\n }\n\n // Use justifyContent to figure out how to allocate the remaining space\n // available in the main axis.\n if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingFreeSpace / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingFreeSpace;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingFreeSpace = fmaxf(remainingFreeSpace, 0);\n if (itemsOnLine > 1) {\n betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingFreeSpace / itemsOnLine;\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim;\n var/*float*/ crossDim = 0;\n\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n if (performLayout) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n }\n } else {\n if (performLayout) {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n }\n \n // Now that we placed the element, we need to update the variables.\n // We need to do that only for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n if (canSkipFlex) {\n // If we skipped the flex step, then we can't rely on the measuredDims because\n // they weren't computed. This means we can't call getDimWithMargin.\n mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis;\n crossDim = availableInnerCrossDim;\n } else {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n \n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n }\n }\n }\n }\n\n mainDim += trailingPaddingAndBorderMain;\n \n var/*float*/ containerCrossAxis = availableInnerCrossDim;\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n // Compute the cross axis from the max cross dimension of the children.\n containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n \n if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim);\n }\n }\n\n // If there's no flex wrap, the cross dimension is defined by the container.\n if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) {\n crossDim = availableInnerCrossDim;\n }\n\n // Clamp to the min/max size specified on the container.\n crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n\n // STEP 7: CROSS-AXIS ALIGNMENT\n // We can skip child alignment if we're just measuring the container.\n if (performLayout) {\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // If the child is absolutely positioned and has a top/left/bottom/right\n // set, override all the previously computed positions to set it correctly.\n if (isPosDefined(child, leading[crossAxis])) {\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n } else {\n child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross +\n getLeadingMargin(child, crossAxis);\n }\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n \n // If the child uses align stretch, we need to lay it out one more time, this time\n // forcing the cross-axis size to be the computed cross size for the current line.\n if (alignItem === CSS_ALIGN_STRETCH) {\n childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n var/*bool*/ isCrossSizeDefinite = false;\n \n if (isMainAxisRow) {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeight = crossDim;\n } else {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW);\n childWidth = crossDim;\n }\n \n // If the child defines a definite size for its cross axis, there's no need to stretch.\n if (!isCrossSizeDefinite) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch');\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;\n }\n }\n }\n\n totalLineCrossDim += crossDim;\n maxLineMainDim = fmaxf(maxLineMainDim, mainDim);\n\n // Reset variables for new line.\n lineCount++;\n startOfLineIndex = endOfLineIndex;\n endOfLineIndex = startOfLineIndex;\n }\n\n // STEP 8: MULTI-LINE CONTENT ALIGNMENT\n if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) {\n var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (availableInnerCrossDim > totalLineCrossDim) {\n crossDimLead = (remainingAlignContentDim / lineCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < lineCount; ++i) {\n var/*int*/ startIndex = endIndex;\n var/*int*/ j;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (j = startIndex; j < childCount; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(lineHeight,\n child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis));\n }\n }\n endIndex = j;\n lineHeight += crossDimLead;\n\n if (performLayout) {\n for (j = startIndex; j < endIndex; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n childHeight = child.layout[measuredDim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with indefinite\n // (auto) crossAxis dimension.\n }\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n // STEP 9: COMPUTING FINAL DIMENSIONS\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n\n // If the user didn't specify a width or height for the node, set the\n // dimensions based on the children.\n if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);\n } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[mainAxis]] = fmaxf(\n fminf(availableInnerMainDim + paddingAndBorderAxisMain,\n boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),\n paddingAndBorderAxisMain);\n }\n\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);\n } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[crossAxis]] = fmaxf(\n fminf(availableInnerCrossDim + paddingAndBorderAxisCross,\n boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),\n paddingAndBorderAxisCross);\n }\n \n // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN\n if (performLayout) {\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n\n // Set trailing position if necessary.\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n }\n \n // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== undefined) {\n // Now that we know the bounds of the container, perform layout again on the\n // absolutely-positioned children.\n if (performLayout) {\n\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n\n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n } else {\n // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) {\n childWidth = node.layout.measuredWidth -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) -\n (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]);\n childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);\n }\n }\n \n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n } else {\n // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) {\n childHeight = node.layout.measuredHeight -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) -\n (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]);\n childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);\n }\n }\n\n // If we're still missing one or the other dimension, measure the content.\n if (isUndefined(childWidth) || isUndefined(childHeight)) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure');\n childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout');\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]);\n }\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]);\n }\n }\n\n currentAbsoluteChild = currentAbsoluteChild.nextChild;\n }\n }\n \n function canUseCachedMeasurement(availableWidth, availableHeight,\n marginRow, marginColumn,\n widthMeasureMode, heightMeasureMode,\n cachedLayout) {\n\n // Is it an exact match?\n if (cachedLayout.availableWidth === availableWidth &&\n cachedLayout.availableHeight === availableHeight &&\n cachedLayout.widthMeasureMode === widthMeasureMode &&\n cachedLayout.heightMeasureMode === heightMeasureMode) {\n return true;\n }\n \n // If the width is an exact match, try a fuzzy match on the height.\n if (cachedLayout.availableWidth === availableWidth &&\n cachedLayout.widthMeasureMode === widthMeasureMode &&\n heightMeasureMode === CSS_MEASURE_MODE_EXACTLY &&\n availableHeight - marginColumn === cachedLayout.computedHeight) {\n return true;\n }\n \n // If the height is an exact match, try a fuzzy match on the width.\n if (cachedLayout.availableHeight === availableHeight &&\n cachedLayout.heightMeasureMode === heightMeasureMode &&\n widthMeasureMode === CSS_MEASURE_MODE_EXACTLY &&\n availableWidth - marginRow === cachedLayout.computedWidth) {\n return true;\n }\n\n return false;\n }\n \n //\n // This is a wrapper around the layoutNodeImpl function. It determines\n // whether the layout request is redundant and can be skipped.\n //\n // Parameters:\n // Input parameters are the same as layoutNodeImpl (see above)\n // Return parameter is true if layout was performed, false if skipped\n //\n function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection,\n widthMeasureMode, heightMeasureMode, performLayout, reason) {\n var layout = node.layout;\n\n var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) ||\n layout.lastParentDirection !== parentDirection;\n\n if (needToVisitNode) {\n // Invalidate the cached results.\n if (layout.cachedMeasurements !== undefined) {\n layout.cachedMeasurements = []; \n }\n if (layout.cachedLayout !== undefined) {\n layout.cachedLayout.widthMeasureMode = undefined;\n layout.cachedLayout.heightMeasureMode = undefined;\n }\n }\n \n var i;\n var len;\n var cachedResults;\n \n // Determine whether the results are already cached. We maintain a separate\n // cache for layouts and measurements. A layout operation modifies the positions\n // and dimensions for nodes in the subtree. The algorithm assumes that each node\n // gets layed out a maximum of one time per tree layout, but multiple measurements\n // may be required to resolve all of the flex dimensions.\n // We handle nodes with measure functions specially here because they are the most\n // expensive to measure, so it's worth avoiding redundant measurements if at all possible.\n if (isMeasureDefined(node)) {\n var marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n \n // First, try to use the layout cache.\n if (layout.cachedLayout &&\n canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,\n widthMeasureMode, heightMeasureMode, layout.cachedLayout)) {\n cachedResults = layout.cachedLayout;\n } else if (layout.cachedMeasurements) {\n // Try to use the measurement cache.\n for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,\n widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i])) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n } else if (performLayout) {\n if (layout.cachedLayout &&\n layout.cachedLayout.availableWidth === availableWidth &&\n layout.cachedLayout.availableHeight === availableHeight &&\n layout.cachedLayout.widthMeasureMode === widthMeasureMode &&\n layout.cachedLayout.heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedLayout;\n }\n } else if (layout.cachedMeasurements) {\n for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (layout.cachedMeasurements[i].availableWidth === availableWidth &&\n layout.cachedMeasurements[i].availableHeight === availableHeight &&\n layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode &&\n layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n \n if (!needToVisitNode && cachedResults !== undefined) {\n layout.measureWidth = cachedResults.computedWidth;\n layout.measureHeight = cachedResults.computedHeight;\n } else {\n layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);\n layout.lastParentDirection = parentDirection;\n \n if (cachedResults === undefined) {\n var newCacheEntry;\n if (performLayout) {\n // Use the single layout cache entry.\n if (layout.cachedLayout === undefined) {\n layout.cachedLayout = {};\n }\n newCacheEntry = layout.cachedLayout;\n } else {\n // Allocate a new measurement cache entry.\n if (layout.cachedMeasurements === undefined) {\n layout.cachedMeasurements = [];\n }\n newCacheEntry = {};\n layout.cachedMeasurements.push(newCacheEntry);\n }\n \n newCacheEntry.availableWidth = availableWidth;\n newCacheEntry.availableHeight = availableHeight;\n newCacheEntry.widthMeasureMode = widthMeasureMode;\n newCacheEntry.heightMeasureMode = heightMeasureMode;\n newCacheEntry.computedWidth = layout.measuredWidth;\n newCacheEntry.computedHeight = layout.measuredHeight;\n }\n }\n \n if (performLayout) {\n node.layout.width = node.layout.measuredWidth;\n node.layout.height = node.layout.measuredHeight;\n layout.shouldUpdate = true;\n }\n \n layout.generationCount = gCurrentGenerationCount;\n return (needToVisitNode || cachedResults === undefined);\n }\n \n function layoutNode(node, availableWidth, availableHeight, parentDirection) {\n // Increment the generation count. This will force the recursive routine to visit\n // all dirty nodes at least once. Subsequent visits will be skipped if the input\n // parameters don't change.\n gCurrentGenerationCount++;\n \n // If the caller didn't specify a height/width, use the dimensions\n // specified in the style.\n if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n }\n if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) {\n setPosition(node, node.layout.direction);\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes,\n canUseCachedMeasurement: canUseCachedMeasurement\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file diff --git a/src/Layout-test-utils.js b/src/Layout-test-utils.js index 6af941e6..f33665d3 100644 --- a/src/Layout-test-utils.js +++ b/src/Layout-test-utils.js @@ -116,6 +116,7 @@ var layoutTestUtils = (function() { if (typeof computeLayout === 'object') { var fillNodes = computeLayout.fillNodes; var realComputeLayout = computeLayout.computeLayout; + var canUseCachedMeasurement = computeLayout.canUseCachedMeasurement; } function extractNodes(node) { @@ -324,6 +325,22 @@ var layoutTestUtils = (function() { function testExtractNodes(node, extractedNode) { expect(extractNodes(node)).toEqual(extractedNode); } + + function testCanUseCachedMeasurement(canReuse, spec, cacheEntry) { + var availableWidth = spec.availableWidth; + var availableHeight = spec.availableHeight; + var widthMeasureMode = spec.widthMeasureMode; + var heightMeasureMode = spec.heightMeasureMode; + + expect( + canUseCachedMeasurement( + availableWidth, availableHeight, + 0, 0, + widthMeasureMode, heightMeasureMode, + cacheEntry + ) + ).toEqual(canReuse); + } function testNamedLayout(name, layoutA, layoutB) { expect(nameLayout(name, layoutA)) @@ -504,6 +521,7 @@ var layoutTestUtils = (function() { }, testFillNodes: testFillNodes, testExtractNodes: testExtractNodes, + testCanUseCachedMeasurement: testCanUseCachedMeasurement, testRandomLayout: function(node) { var layout = computeCSSLayout(node); var domLayout = computeDOMLayout(node); diff --git a/src/Layout.c b/src/Layout.c index b001efab..4a064512 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -1516,6 +1516,37 @@ static const char* getModeName(css_measure_mode_t mode, bool performLayout) { return performLayout? kLayoutModeNames[mode] : kMeasureModeNames[mode]; } +static bool canUseCachedMeasurement(float availableWidth, float availableHeight, + float marginRow, float marginColumn, + css_measure_mode_t widthMeasureMode, css_measure_mode_t heightMeasureMode, + css_cached_measurement_t cachedLayout) { + + // Is it an exact match? + if (eq(cachedLayout.available_width, availableWidth) && + eq(cachedLayout.available_height, availableHeight) && + cachedLayout.width_measure_mode == widthMeasureMode && + cachedLayout.height_measure_mode == heightMeasureMode) { + return true; + } + + // If the width is an exact match, try a fuzzy match on the height. + if (cachedLayout.width_measure_mode == widthMeasureMode && + eq(cachedLayout.available_width, availableWidth) && + heightMeasureMode == CSS_MEASURE_MODE_EXACTLY && + eq(availableHeight - marginColumn, cachedLayout.computed_height)) { + return true; + } + + // If the height is an exact match, try a fuzzy match on the width. + if (cachedLayout.height_measure_mode == heightMeasureMode && + eq(cachedLayout.available_height, availableHeight) && + widthMeasureMode == CSS_MEASURE_MODE_EXACTLY && + eq(availableWidth - marginRow, cachedLayout.computed_width)) { + return true; + } + + return false; +} // // This is a wrapper around the layoutNodeImpl function. It determines @@ -1548,7 +1579,27 @@ bool layoutNodeInternal(css_node_t* node, float availableWidth, float availableH // and dimensions for nodes in the subtree. The algorithm assumes that each node // gets layed out a maximum of one time per tree layout, but multiple measurements // may be required to resolve all of the flex dimensions. - if (performLayout) { + // We handle nodes with measure functions specially here because they are the most + // expensive to measure, so it's worth avoiding redundant measurements if at all possible. + if (isMeasureDefined(node)) { + float marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + float marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + + // First, try to use the layout cache. + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout->cached_layout)) { + cachedResults = &layout->cached_layout; + } else { + // Try to use the measurement cache. + for (int i = 0; i < layout->next_cached_measurements_index; i++) { + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout->cached_measurements[i])) { + cachedResults = &layout->cached_measurements[i]; + break; + } + } + } + } else if (performLayout) { if (eq(layout->cached_layout.available_width, availableWidth) && eq(layout->cached_layout.available_height, availableHeight) && layout->cached_layout.width_measure_mode == widthMeasureMode && diff --git a/src/Layout.js b/src/Layout.js index 056ac0d9..cc07aa08 100755 --- a/src/Layout.js +++ b/src/Layout.js @@ -1431,6 +1431,38 @@ var computeLayout = (function() { } } + function canUseCachedMeasurement(availableWidth, availableHeight, + marginRow, marginColumn, + widthMeasureMode, heightMeasureMode, + cachedLayout) { + + // Is it an exact match? + if (cachedLayout.availableWidth === availableWidth && + cachedLayout.availableHeight === availableHeight && + cachedLayout.widthMeasureMode === widthMeasureMode && + cachedLayout.heightMeasureMode === heightMeasureMode) { + return true; + } + + // If the width is an exact match, try a fuzzy match on the height. + if (cachedLayout.availableWidth === availableWidth && + cachedLayout.widthMeasureMode === widthMeasureMode && + heightMeasureMode === CSS_MEASURE_MODE_EXACTLY && + availableHeight - marginColumn === cachedLayout.computedHeight) { + return true; + } + + // If the height is an exact match, try a fuzzy match on the width. + if (cachedLayout.availableHeight === availableHeight && + cachedLayout.heightMeasureMode === heightMeasureMode && + widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && + availableWidth - marginRow === cachedLayout.computedWidth) { + return true; + } + + return false; + } + // // This is a wrapper around the layoutNodeImpl function. It determines // whether the layout request is redundant and can be skipped. @@ -1456,7 +1488,9 @@ var computeLayout = (function() { layout.cachedLayout.heightMeasureMode = undefined; } } - + + var i; + var len; var cachedResults; // Determine whether the results are already cached. We maintain a separate @@ -1464,7 +1498,28 @@ var computeLayout = (function() { // and dimensions for nodes in the subtree. The algorithm assumes that each node // gets layed out a maximum of one time per tree layout, but multiple measurements // may be required to resolve all of the flex dimensions. - if (performLayout) { + // We handle nodes with measure functions specially here because they are the most + // expensive to measure, so it's worth avoiding redundant measurements if at all possible. + if (isMeasureDefined(node)) { + var marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW); + var marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN); + + // First, try to use the layout cache. + if (layout.cachedLayout && + canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout.cachedLayout)) { + cachedResults = layout.cachedLayout; + } else if (layout.cachedMeasurements) { + // Try to use the measurement cache. + for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) { + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i])) { + cachedResults = layout.cachedMeasurements[i]; + break; + } + } + } + } else if (performLayout) { if (layout.cachedLayout && layout.cachedLayout.availableWidth === availableWidth && layout.cachedLayout.availableHeight === availableHeight && @@ -1473,7 +1528,7 @@ var computeLayout = (function() { cachedResults = layout.cachedLayout; } } else if (layout.cachedMeasurements) { - for (var i = 0, len = layout.cachedMeasurements.length; i < len; i++) { + for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) { if (layout.cachedMeasurements[i].availableWidth === availableWidth && layout.cachedMeasurements[i].availableHeight === availableHeight && layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode && @@ -1553,7 +1608,8 @@ var computeLayout = (function() { return { layoutNodeImpl: layoutNodeImpl, computeLayout: layoutNode, - fillNodes: fillNodes + fillNodes: fillNodes, + canUseCachedMeasurement: canUseCachedMeasurement }; })(); diff --git a/src/__tests__/Layout-test.js b/src/__tests__/Layout-test.js index 738fd40c..391c518c 100755 --- a/src/__tests__/Layout-test.js +++ b/src/__tests__/Layout-test.js @@ -12,6 +12,7 @@ var testLayout = layoutTestUtils.testLayout; var testLayoutAgainstDomOnly = layoutTestUtils.testLayoutAgainstDomOnly; var testLayoutAgainstExpectedOnly = layoutTestUtils.testLayoutAgainstExpectedOnly; var testFillNodes = layoutTestUtils.testFillNodes; +var testCanUseCachedMeasurement = layoutTestUtils.testCanUseCachedMeasurement; var text = layoutTestUtils.text; var texts = layoutTestUtils.texts; var textSizes = layoutTestUtils.textSizes; @@ -38,6 +39,152 @@ describe('Javascript Only', function() { {layout: {width: 200}}, {layout: {width: 200}, style: {}, children: []} ); + }); + it('should only invoke measure function one time in simple layout', function() { + var measureInvocations = 0; + function measure(width, widthMode, height, heightMode) { + measureInvocations++; + return { width: 25, height: 25 }; + } + + testLayoutAgainstExpectedOnly( + {style: {width: 100, height: 100}, children: [ + {style: {measure: measure}} + ]}, + {width: 100, height: 100, top: 0, left: 0, children: [ + {width: 100, height: 25, top: 0, left: 0} + ]} + ); + + expect(measureInvocations).toEqual(1); + }); +}); + +describe('JavaScript Only: canUseCachedTextMeasurement', function() { + var measureModes = ['undefined', 'exactly', 'at-most']; + + var assertCanReuse = testCanUseCachedMeasurement.bind(null, true); + var assertCannotReuse = testCanUseCachedMeasurement.bind(null, false); + + it('should not reuse when width mode is "exactly" and available width != measurement', function() { + measureModes.forEach(function(widthMeasureMode) { + measureModes.forEach(function(heightMeasureMode) { + var computedWidth = 100; + var computedHeight = 200; + var availableWidth = widthMeasureMode === 'undefined' ? undefined : computedWidth; + var availableHeight = heightMeasureMode === 'undefined' ? undefined : computedHeight; + + var cacheEntry = { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, computedWidth: computedWidth, + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode, computedHeight: computedHeight + }; + + assertCannotReuse( + { + availableWidth: computedWidth - 1, widthMeasureMode: 'exactly', + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode + }, + cacheEntry + ); + + assertCannotReuse( + { + availableWidth: computedWidth + 1, widthMeasureMode: 'exactly', + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode + }, + cacheEntry + ); + }); + }); + }); + + it('should not reuse when height mode is "exactly" and available height != measurement', function() { + measureModes.forEach(function(widthMeasureMode) { + measureModes.forEach(function(heightMeasureMode) { + var computedWidth = 100; + var computedHeight = 200; + var availableWidth = widthMeasureMode === 'undefined' ? undefined : computedWidth; + var availableHeight = heightMeasureMode === 'undefined' ? undefined : computedHeight; + + var cacheEntry = { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, computedWidth: computedWidth, + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode, computedHeight: computedHeight + }; + + assertCannotReuse( + { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, + availableHeight: computedHeight - 1, heightMeasureMode: 'exactly' + }, + cacheEntry + ); + + assertCannotReuse( + { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, + availableHeight: computedHeight + 1, heightMeasureMode: 'exactly' + }, + cacheEntry + ); + }); + }); + }); + + it('should reuse exact matches', function() { + measureModes.forEach(function(widthMeasureMode) { + measureModes.forEach(function(heightMeasureMode) { + var availableWidth = widthMeasureMode === 'undefined' ? undefined : 100; + var availableHeight = heightMeasureMode === 'undefined' ? undefined : 200; + assertCanReuse( + { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode + }, + { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, computedWidth: 1, + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode, computedHeight: 2 + } + ); + }); + }); + }); + + it('should reuse cache entry with unconstrained width when width mode is "exactly" and available width == measurement', function() { + measureModes.forEach(function(heightMeasureMode) { + var computedWidth = 100; + var computedHeight = 200; + var availableHeight = heightMeasureMode === 'undefined' ? undefined : computedHeight; + + assertCanReuse( + { + availableWidth: computedWidth, widthMeasureMode: 'exactly', + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode + }, + { + availableWidth: undefined, widthMeasureMode: 'undefined', computedWidth: computedWidth, + availableHeight: availableHeight, heightMeasureMode: heightMeasureMode, computedHeight: computedHeight + } + ); + }); + }); + + it('should reuse cache entry with unconstrained height when height mode is "exactly" and height == measurement', function() { + measureModes.forEach(function(widthMeasureMode) { + var computedWidth = 100; + var computedHeight = 200; + var availableWidth = widthMeasureMode === 'undefined' ? undefined : computedWidth; + + assertCanReuse( + { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, + availableHeight: computedHeight, heightMeasureMode: 'exactly' + }, + { + availableWidth: availableWidth, widthMeasureMode: widthMeasureMode, computedWidth: computedWidth, + availableHeight: undefined, heightMeasureMode: 'undefined', computedHeight: computedHeight + } + ); + }); }); }); diff --git a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs index 3d9924e5..551e0fb3 100644 --- a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs +++ b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs @@ -281,7 +281,42 @@ namespace Facebook.CSSLayout setPosition(node, node.layout.direction); } } + + internal static boolean canUseCachedMeasurement(float availableWidth, float availableHeight, + float marginRow, float marginColumn, + CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, + CSSCachedMeasurement cachedLayout) + { + // Is it an exact match? + if (FloatUtil.floatsEqual(cachedLayout.availableWidth, availableWidth) && + FloatUtil.floatsEqual(cachedLayout.availableHeight, availableHeight) && + cachedLayout.widthMeasureMode == widthMeasureMode && + cachedLayout.heightMeasureMode == heightMeasureMode) + { + return true; + } + + // If the width is an exact match, try a fuzzy match on the height. + if (FloatUtil.floatsEqual(cachedLayout.availableWidth, availableWidth) && + cachedLayout.widthMeasureMode == widthMeasureMode && + heightMeasureMode == CSSMeasureMode.Exactly && + FloatUtil.floatsEqual(availableHeight - marginColumn, cachedLayout.computedHeight)) + { + return true; + } + + // If the height is an exact match, try a fuzzy match on the width. + if (FloatUtil.floatsEqual(cachedLayout.availableHeight, availableHeight) && + cachedLayout.heightMeasureMode == heightMeasureMode && + widthMeasureMode == CSSMeasureMode.Exactly && + FloatUtil.floatsEqual(availableWidth - marginRow, cachedLayout.computedWidth)) + { + return true; + } + return false; + } + // // This is a wrapper around the layoutNodeImpl function. It determines // whether the layout request is redundant and can be skipped. @@ -312,7 +347,38 @@ namespace Facebook.CSSLayout // and dimensions for nodes in the subtree. The algorithm assumes that each node // gets layed out a maximum of one time per tree layout, but multiple measurements // may be required to resolve all of the flex dimensions. - if (performLayout) + // We handle nodes with measure functions specially here because they are the most + // expensive to measure, so it's worth avoiding redundant measurements if at all possible. + if (isMeasureDefined(node)) + { + float marginAxisRow = + node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]); + float marginAxisColumn = + node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]); + + // First, try to use the layout cache. + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout.cachedLayout)) + { + cachedResults = layout.cachedLayout; + } + else + { + // Try to use the measurement cache. + for (int i = 0; i < layout.nextCachedMeasurementsIndex; i++) + { + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i])) + { + cachedResults = layout.cachedMeasurements[i]; + break; + } + } + } + } + else if (performLayout) { if (FloatUtil.floatsEqual(layout.cachedLayout.availableWidth, availableWidth) && FloatUtil.floatsEqual(layout.cachedLayout.availableHeight, availableHeight) && diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index fa9c35ef..280b542a 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -250,6 +250,37 @@ public class LayoutEngine { } } + /*package*/ static boolean canUseCachedMeasurement(float availableWidth, float availableHeight, + float marginRow, float marginColumn, + CSSMeasureMode widthMeasureMode, CSSMeasureMode heightMeasureMode, + CSSCachedMeasurement cachedLayout) { + // Is it an exact match? + if (FloatUtil.floatsEqual(cachedLayout.availableWidth, availableWidth) && + FloatUtil.floatsEqual(cachedLayout.availableHeight, availableHeight) && + cachedLayout.widthMeasureMode == widthMeasureMode && + cachedLayout.heightMeasureMode == heightMeasureMode) { + return true; + } + + // If the width is an exact match, try a fuzzy match on the height. + if (FloatUtil.floatsEqual(cachedLayout.availableWidth, availableWidth) && + cachedLayout.widthMeasureMode == widthMeasureMode && + heightMeasureMode == CSSMeasureMode.EXACTLY && + FloatUtil.floatsEqual(availableHeight - marginColumn, cachedLayout.computedHeight)) { + return true; + } + + // If the height is an exact match, try a fuzzy match on the width. + if (FloatUtil.floatsEqual(cachedLayout.availableHeight, availableHeight) && + cachedLayout.heightMeasureMode == heightMeasureMode && + widthMeasureMode == CSSMeasureMode.EXACTLY && + FloatUtil.floatsEqual(availableWidth - marginRow, cachedLayout.computedWidth)) { + return true; + } + + return false; + } + // // This is a wrapper around the layoutNodeImpl function. It determines // whether the layout request is redundant and can be skipped. @@ -287,7 +318,31 @@ public class LayoutEngine { // and dimensions for nodes in the subtree. The algorithm assumes that each node // gets layed out a maximum of one time per tree layout, but multiple measurements // may be required to resolve all of the flex dimensions. - if (performLayout) { + // We handle nodes with measure functions specially here because they are the most + // expensive to measure, so it's worth avoiding redundant measurements if at all possible. + if (isMeasureDefined(node)) { + float marginAxisRow = + node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_ROW], leading[CSS_FLEX_DIRECTION_ROW]) + + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_ROW], trailing[CSS_FLEX_DIRECTION_ROW]); + float marginAxisColumn = + node.style.margin.getWithFallback(leadingSpacing[CSS_FLEX_DIRECTION_COLUMN], leading[CSS_FLEX_DIRECTION_COLUMN]) + + node.style.margin.getWithFallback(trailingSpacing[CSS_FLEX_DIRECTION_COLUMN], trailing[CSS_FLEX_DIRECTION_COLUMN]); + + // First, try to use the layout cache. + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout.cachedLayout)) { + cachedResults = layout.cachedLayout; + } else { + // Try to use the measurement cache. + for (int i = 0; i < layout.nextCachedMeasurementsIndex; i++) { + if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn, + widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i])) { + cachedResults = layout.cachedMeasurements[i]; + break; + } + } + } + } else if (performLayout) { if (FloatUtil.floatsEqual(layout.cachedLayout.availableWidth, availableWidth) && FloatUtil.floatsEqual(layout.cachedLayout.availableHeight, availableHeight) && layout.cachedLayout.widthMeasureMode == widthMeasureMode && From 16f43dac87ace8b60e0e4c07a798a558c22bd21b Mon Sep 17 00:00:00 2001 From: Adam Comella Date: Wed, 25 May 2016 10:53:15 -0700 Subject: [PATCH 4/4] Skip measurement when there's no available vertical space We already did this optimization when there wasn't any available horizontal space. Now we're covering the vertical space case as well. This optimization assumes that, for a node with a measure function, if there isn't any available horizontal or vertical space, then we don't need to measure the node and can assume that the node is 0x0. --- dist/css-layout.h | 4 ++-- dist/css-layout.jar | Bin 16326 -> 16323 bytes dist/css-layout.js | 4 ++-- dist/css-layout.min.js | 2 +- dist/css-layout.min.js.map | 2 +- src/Layout.c | 4 ++-- src/Layout.js | 4 ++-- src/csharp/Facebook.CSSLayout/LayoutEngine.cs | 4 ++-- .../com/facebook/csslayout/LayoutEngine.java | 4 ++-- 9 files changed, 14 insertions(+), 14 deletions(-) diff --git a/dist/css-layout.h b/dist/css-layout.h index 002eec12..15aec895 100644 --- a/dist/css-layout.h +++ b/dist/css-layout.h @@ -883,9 +883,9 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // Don't bother sizing the text if both dimensions are already defined. node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - } else if (innerWidth <= 0) { + } else if (innerWidth <= 0 || innerHeight <= 0) { - // Don't bother sizing the text if there's no horizontal space. + // Don't bother sizing the text if there's no horizontal or vertical space. node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { diff --git a/dist/css-layout.jar b/dist/css-layout.jar index 90c02da277cff936784507a12e54360acb2ca9ea..9428bb4c648e538acb46d7202ad365f706ab9990 100644 GIT binary patch delta 271 zcmX?Bf4H7Ez?+#xgn@&DgP|g9=R{sBW)S7&ZLX2=LtItu`4TOQ=i;JI#8sy;T+RAa z^s$JMA;6oRqa{*R(t&}2!4ZfNCal@FYUBA(CT8)lotxE}C$caX0vWs6*BOHuIo4)O zAk!x=wYA^{(jIU_Ccm;Zl?Q6}*js&m^%q7426k2k1~Z`12(W*$ot>)!&|r@#w-1GB zu`n<+N-;3lAQS*u;>?rn?KCFGF^f(1vgZNWIr)Q~G}CH!s4&}PYkO(1aHPF7Q;aoO Nc%Hoy+hkjiIRMCGOZor+ delta 270 zcmX?Hf2^K2z?+#xgn@&DgP}cV(?nh?W)S7&ZFOY!Z0(F6;;L%TmuOi$7Z-gZt~!lj zYWAn1k420O0p9E!tPA+|I5RLXxBxN2kn?r3Hl7J(VwP&2wONIEA`5d<&Zf;f*w+~| zHv$=%)@Do~3nnkJwcrKP9&ke@Keshy25O!xZzs)kh+*;qJB`UHb~5rnb3CTpJ`|?K z!obid#lT<#v<(5ICeOF?23axrEl}G!cAz%B$rkp~3P8&|p1KH!U1Vfnc*M-WV1dvB WWW`&9&0k? diff --git a/dist/css-layout.js b/dist/css-layout.js index 6a05d1c6..d199bd33 100644 --- a/dist/css-layout.js +++ b/dist/css-layout.js @@ -647,9 +647,9 @@ var computeLayout = (function() { // Don't bother sizing the text if both dimensions are already defined. node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - } else if (innerWidth <= 0) { + } else if (innerWidth <= 0 || innerHeight <= 0) { - // Don't bother sizing the text if there's no horizontal space. + // Don't bother sizing the text if there's no horizontal or vertical space. node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { diff --git a/dist/css-layout.min.js b/dist/css-layout.min.js index f5930b8d..9478f032 100644 --- a/dist/css-layout.min.js +++ b/dist/css-layout.min.js @@ -1,2 +1,2 @@ -!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.computeLayout=b()}(this,function(){var a=function(){function a(b){if(b.layout&&!b.isDirty||(b.layout={width:void 0,height:void 0,top:0,left:0,right:0,bottom:0}),b.style||(b.style={}),b.children||(b.children=[]),b.style.measure&&b.children&&b.children.length)throw new Error("Using custom measure function is supported only for leaf nodes.");return b.children.forEach(a),b}function b(a){return void 0===a||Number.isNaN(a)}function c(a){return a===da||a===ea}function d(a){return a===fa||a===ga}function e(a){return void 0===a.style.flex?0:a.style.flex}function f(a){return W?!0:e(a)<=0}function g(a){return e(a)>0?e(a):0}function h(a){if(W){if(0!==e(a))return 1}else if(e(a)<0)return 1;return 0}function i(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function j(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function k(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function l(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function m(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function n(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function o(a,b){return k(a,b)+m(a,b)}function p(a,b){return l(a,b)+n(a,b)}function q(a,b){return i(a,b)+j(a,b)}function r(a,b){return o(a,b)+p(a,b)}function s(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function t(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function u(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function v(a,b){if(b===ca){if(a===da)return ea;if(a===ea)return da}return a}function w(a,b){var c;return c=a.style.direction?a.style.direction:aa,c===aa&&(c=void 0===b?ba:b),c}function x(a){return a.style.flexDirection?a.style.flexDirection:fa}function y(a,b){return d(a)?v(da,b):fa}function z(a){return a.style.position?a.style.position:qa}function A(a){return a.style.overflow?a.style.overflow:sa}function B(a){return z(a)===qa&&void 0!==a.style.flex&&0!==a.style.flex}function C(a){return"wrap"===a.style.flexWrap}function D(a,b){return a.layout[Ba[b]]+q(a,b)}function E(a,b){return void 0!==a.style[Aa[b]]&&a.style[Aa[b]]>=0}function F(a,b){return void 0!==a.layout[Ba[b]]&&a.layout[Ba[b]]>=0}function G(a,b){return void 0!==a.style[b]}function H(a){return void 0!==a.style.measure}function I(a,b){return void 0!==a.style[b]?a.style[b]:0}function J(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function K(a,b){return b>a?a:b}function L(a,b){return a>b?a:b}function M(a,b,c){return L(J(a,b,c),r(a,b))}function N(a,b,c){var d=z(b)===ra?0:b.layout[Ba[c]];b.layout[ya[c]]=a.layout[Ba[c]]-d-b.layout[za[c]]}function O(a,b){return void 0!==a.style[xa[b]]?I(a,xa[b]):-I(a,ya[b])}function P(a,b){var c=v(x(a),b),d=y(c,b);a.layout[xa[c]]=i(a,c)+O(a,c),a.layout[ya[c]]=j(a,c)+O(a,c),a.layout[xa[d]]=i(a,d)+O(a,d),a.layout[ya[d]]=j(a,d)+O(a,d)}function Q(a,b){if(!a)throw new Error(b)}function R(a,d,e,k,l,O,R){Q(b(d)?l===ua:!0,"availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED"),Q(b(e)?O===ua:!0,"availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED");var S=r(a,da),U=r(a,fa),W=q(a,da),X=q(a,fa),aa=w(a,k);if(a.layout.direction=aa,H(a)){var ba=d-W-S,ca=e-X-U;if(l===va&&O===va)a.layout.measuredWidth=M(a,da,d-W),a.layout.measuredHeight=M(a,fa,e-X);else if(0>=ba)a.layout.measuredWidth=M(a,da,0),a.layout.measuredHeight=M(a,fa,0);else{var sa=a.style.measure(ba,l,ca,O);a.layout.measuredWidth=M(a,da,l===ua||l===wa?sa.width+S:d-W),a.layout.measuredHeight=M(a,fa,O===ua||O===wa?sa.height+U:e-X)}}else{var Aa=a.children.length;if(0===Aa)return a.layout.measuredWidth=M(a,da,l===ua||l===wa?S:d-W),void(a.layout.measuredHeight=M(a,fa,O===ua||O===wa?U:e-X));if(!R){if(l===wa&&0>=d&&O===wa&&0>=e)return a.layout.measuredWidth=M(a,da,0),void(a.layout.measuredHeight=M(a,fa,0));if(l===wa&&0>=d)return a.layout.measuredWidth=M(a,da,0),void(a.layout.measuredHeight=M(a,fa,b(e)?0:e-X));if(O===wa&&0>=e)return a.layout.measuredWidth=M(a,da,b(d)?0:d-W),void(a.layout.measuredHeight=M(a,fa,0));if(l===va&&O===va)return a.layout.measuredWidth=M(a,da,d-W),void(a.layout.measuredHeight=M(a,fa,e-X))}var Ca,Da,Ea,Fa,Ga,Ha,Ia=v(x(a),aa),Ja=y(Ia,aa),Ka=c(Ia),La=s(a),Ma=C(a),Na=void 0,Oa=void 0,Pa=o(a,Ia),Qa=p(a,Ia),Ra=o(a,Ja),Sa=r(a,Ia),Ta=r(a,Ja),Ua=Ka?l:O,Va=Ka?O:l,Wa=d-W-S,Xa=e-X-U,Ya=Ka?Wa:Xa,Za=Ka?Xa:Wa;for(Da=0;Aa>Da;Da++){if(Ca=a.children[Da],R){var $a=w(Ca,aa);P(Ca,$a)}z(Ca)===ra?(void 0===Na&&(Na=Ca),void 0!==Oa&&(Oa.nextChild=Ca),Oa=Ca,Ca.nextChild=void 0):Ka&&E(Ca,da)?Ca.layout.flexBasis=L(Ca.style.width,r(Ca,da)):!Ka&&E(Ca,fa)?Ca.layout.flexBasis=L(Ca.style.height,r(Ca,fa)):f(Ca)||b(Ya)?(Ea=V,Fa=V,Ga=ua,Ha=ua,E(Ca,da)&&(Ea=Ca.style.width+q(Ca,da),Ga=va),E(Ca,fa)&&(Fa=Ca.style.height+q(Ca,fa),Ha=va),Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=wa),A(a)===ta&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=wa),T(Ca,Ea,Fa,aa,Ga,Ha,!1,"measure"),Ca.layout.flexBasis=L(Ka?Ca.layout.measuredWidth:Ca.layout.measuredHeight,r(Ca,Ia))):Ca.layout.flexBasis=L(0,r(Ca,Ia))}for(var _a=0,ab=0,bb=0,cb=0,db=0;Aa>ab;){var eb=0,fb=0,gb=0,hb=0;Da=_a;for(var ib=void 0,jb=void 0;Aa>Da;){if(Ca=a.children[Da],Ca.lineIndex=bb,z(Ca)!==ra){var kb=Ca.layout.flexBasis+q(Ca,Ia);if(fb+kb>Ya&&Ma&&eb>0)break;fb+=kb,eb++,B(Ca)&&(gb+=g(Ca),hb+=h(Ca)*Ca.layout.flexBasis),void 0===ib&&(ib=Ca),void 0!==jb&&(jb.nextChild=Ca),jb=Ca,Ca.nextChild=void 0}Da++,ab++}var lb=!R&&Va===va,mb=0,nb=0,ob=0;b(Ya)?0>fb&&(ob=-fb):ob=Ya-fb;var pb=ob,qb=0;if(!lb){var rb,sb,tb,ub,vb,wb=0,xb=0;for(jb=ib;void 0!==jb;)rb=jb.layout.flexBasis,0>ob?(sb=h(jb)*rb,0!==sb&&(ub=rb+ob/hb*sb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,wb-=sb))):ob>0&&(tb=g(jb),0!==tb&&(ub=rb+ob/gb*tb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,xb-=tb))),jb=jb.nextChild;for(hb+=wb,gb+=xb,ob+=qb,qb=0,jb=ib;void 0!==jb;){rb=jb.layout.flexBasis;var yb=rb;0>ob?(sb=h(jb)*rb,0!==sb&&(yb=M(jb,Ia,rb+ob/hb*sb))):ob>0&&(tb=g(jb),0!==tb&&(yb=M(jb,Ia,rb+ob/gb*tb))),qb-=yb-rb,Ka?(Ea=yb+q(jb,da),Ga=va,E(jb,fa)?(Fa=jb.style.height+q(jb,fa),Ha=va):(Fa=Za,Ha=b(Fa)?ua:wa)):(Fa=yb+q(jb,fa),Ha=va,E(jb,da)?(Ea=jb.style.width+q(jb,da),Ga=va):(Ea=Za,Ga=b(Ea)?ua:wa));var zb=!E(jb,Ja)&&u(a,jb)===pa;T(jb,Ea,Fa,aa,Ga,Ha,R&&!zb,"flex"),jb=jb.nextChild}}ob=pb+qb,Ua===wa&&(ob=0),La!==ha&&(La===ia?mb=ob/2:La===ja?mb=ob:La===ka?(ob=L(ob,0),nb=eb>1?ob/(eb-1):0):La===la&&(nb=ob/eb,mb=nb/2));var Ab=Pa+mb,Bb=0;for(Da=_a;ab>Da;++Da)Ca=a.children[Da],z(Ca)===ra&&G(Ca,xa[Ia])?R&&(Ca.layout[za[Ia]]=I(Ca,xa[Ia])+m(a,Ia)+i(Ca,Ia)):(R&&(Ca.layout[za[Ia]]+=Ab),z(Ca)===qa&&(lb?(Ab+=nb+q(Ca,Ia)+Ca.layout.flexBasis,Bb=Za):(Ab+=nb+D(Ca,Ia),Bb=L(Bb,D(Ca,Ja)))));Ab+=Qa;var Cb=Za;if(Va!==ua&&Va!==wa||(Cb=M(a,Ja,Bb+Ta)-Ta,Va===wa&&(Cb=K(Cb,Za))),Ma||Va!==va||(Bb=Za),Bb=M(a,Ja,Bb+Ta)-Ta,R)for(Da=_a;ab>Da;++Da)if(Ca=a.children[Da],z(Ca)===ra)G(Ca,xa[Ja])?Ca.layout[za[Ja]]=I(Ca,xa[Ja])+m(a,Ja)+i(Ca,Ja):Ca.layout[za[Ja]]=Ra+i(Ca,Ja);else{var Db=Ra,Eb=u(a,Ca);if(Eb===pa){Ea=Ca.layout.measuredWidth+q(Ca,da),Fa=Ca.layout.measuredHeight+q(Ca,fa);var Fb=!1;Ka?(Fb=E(Ca,fa),Fa=Bb):(Fb=E(Ca,da),Ea=Bb),Fb||(Ga=b(Ea)?ua:va,Ha=b(Fa)?ua:va,T(Ca,Ea,Fa,aa,Ga,Ha,!0,"stretch"))}else if(Eb!==ma){var Gb=Cb-D(Ca,Ja);Db+=Eb===na?Gb/2:Gb}Ca.layout[za[Ja]]+=cb+Db}cb+=Bb,db=L(db,Ab),bb++,_a=ab,ab=_a}if(bb>1&&R&&!b(Za)){var Hb=Za-cb,Ib=0,Jb=Ra,Kb=t(a);Kb===oa?Jb+=Hb:Kb===na?Jb+=Hb/2:Kb===pa&&Za>cb&&(Ib=Hb/bb);var Lb=0;for(Da=0;bb>Da;++Da){var Mb,Nb=Lb,Ob=0;for(Mb=Nb;Aa>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===qa){if(Ca.lineIndex!==Da)break;F(Ca,Ja)&&(Ob=L(Ob,Ca.layout[Ba[Ja]]+q(Ca,Ja)))}if(Lb=Mb,Ob+=Ib,R)for(Mb=Nb;Lb>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===qa){var Pb=u(a,Ca);Pb===ma?Ca.layout[za[Ja]]=Jb+i(Ca,Ja):Pb===oa?Ca.layout[za[Ja]]=Jb+Ob-j(Ca,Ja)-Ca.layout[Ba[Ja]]:Pb===na?(Fa=Ca.layout[Ba[Ja]],Ca.layout[za[Ja]]=Jb+(Ob-Fa)/2):Pb===pa&&(Ca.layout[za[Ja]]=Jb+i(Ca,Ja))}Jb+=Ob}}if(a.layout.measuredWidth=M(a,da,d-W),a.layout.measuredHeight=M(a,fa,e-X),Ua===ua?a.layout[Ba[Ia]]=M(a,Ia,db):Ua===wa&&(a.layout[Ba[Ia]]=L(K(Ya+Sa,J(a,Ia,db)),Sa)),Va===ua?a.layout[Ba[Ja]]=M(a,Ja,cb+Ta):Va===wa&&(a.layout[Ba[Ja]]=L(K(Za+Ta,J(a,Ja,cb+Ta)),Ta)),R){var Qb=!1,Rb=!1;if(Ia!==ea&&Ia!==ga||(Qb=!0),Ja!==ea&&Ja!==ga||(Rb=!0),Qb||Rb)for(Da=0;Aa>Da;++Da)Ca=a.children[Da],Qb&&N(a,Ca,Ia),Rb&&N(a,Ca,Ja)}for(Oa=Na;void 0!==Oa;)R&&(Ea=V,Fa=V,E(Oa,da)?Ea=Oa.style.width+q(Oa,da):G(Oa,Y)&&G(Oa,$)&&(Ea=a.layout.measuredWidth-(m(a,da)+n(a,da))-(Oa.style[Y]+Oa.style[$]),Ea=M(Oa,da,Ea)),E(Oa,fa)?Fa=Oa.style.height+q(Oa,fa):G(Oa,Z)&&G(Oa,_)&&(Fa=a.layout.measuredHeight-(m(a,fa)+n(a,fa))-(Oa.style[Z]+Oa.style[_]),Fa=M(Oa,fa,Fa)),(b(Ea)||b(Fa))&&(Ga=b(Ea)?ua:va,Ha=b(Fa)?ua:va,Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=wa),A(a)===ta&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=wa),T(Oa,Ea,Fa,aa,Ga,Ha,!1,"abs-measure"),Ea=Oa.layout.measuredWidth+q(Oa,da),Fa=Oa.layout.measuredHeight+q(Oa,fa)),T(Oa,Ea,Fa,aa,va,va,!0,"abs-layout"),G(Oa,ya[da])&&!G(Oa,xa[da])&&(Oa.layout[xa[da]]=a.layout[Ba[da]]-Oa.layout[Ba[da]]-I(Oa,ya[da])),G(Oa,ya[fa])&&!G(Oa,xa[fa])&&(Oa.layout[xa[fa]]=a.layout[Ba[fa]]-Oa.layout[Ba[fa]]-I(Oa,ya[fa]))),Oa=Oa.nextChild}}function S(a,b,c,d,e,f,g){return g.availableWidth===a&&g.availableHeight===b&&g.widthMeasureMode===e&&g.heightMeasureMode===f?!0:g.availableWidth===a&&g.widthMeasureMode===e&&f===va&&b-d===g.computedHeight?!0:g.availableHeight===b&&g.heightMeasureMode===f&&e===va&&a-c===g.computedWidth}function T(a,b,c,d,e,f,g,h){var i=a.layout,j=a.isDirty&&i.generationCount!==X||i.lastParentDirection!==d;j&&(void 0!==i.cachedMeasurements&&(i.cachedMeasurements=[]),void 0!==i.cachedLayout&&(i.cachedLayout.widthMeasureMode=void 0,i.cachedLayout.heightMeasureMode=void 0));var k,l,m;if(H(a)){var n=q(a,da),o=q(a,fa);if(i.cachedLayout&&S(b,c,n,o,e,f,i.cachedLayout))m=i.cachedLayout;else if(i.cachedMeasurements)for(k=0,l=i.cachedMeasurements.length;l>k;k++)if(S(b,c,n,o,e,f,i.cachedMeasurements[k])){m=i.cachedMeasurements[k];break}}else if(g)i.cachedLayout&&i.cachedLayout.availableWidth===b&&i.cachedLayout.availableHeight===c&&i.cachedLayout.widthMeasureMode===e&&i.cachedLayout.heightMeasureMode===f&&(m=i.cachedLayout);else if(i.cachedMeasurements)for(k=0,l=i.cachedMeasurements.length;l>k;k++)if(i.cachedMeasurements[k].availableWidth===b&&i.cachedMeasurements[k].availableHeight===c&&i.cachedMeasurements[k].widthMeasureMode===e&&i.cachedMeasurements[k].heightMeasureMode===f){m=i.cachedMeasurements[k];break}if(j||void 0===m){if(R(a,b,c,d,e,f,g),i.lastParentDirection=d,void 0===m){var p;g?(void 0===i.cachedLayout&&(i.cachedLayout={}),p=i.cachedLayout):(void 0===i.cachedMeasurements&&(i.cachedMeasurements=[]),p={},i.cachedMeasurements.push(p)),p.availableWidth=b,p.availableHeight=c,p.widthMeasureMode=e,p.heightMeasureMode=f,p.computedWidth=i.measuredWidth,p.computedHeight=i.measuredHeight}}else i.measureWidth=m.computedWidth,i.measureHeight=m.computedHeight;return g&&(a.layout.width=a.layout.measuredWidth,a.layout.height=a.layout.measuredHeight,i.shouldUpdate=!0),i.generationCount=X,j||void 0===m}function U(a,c,d,e){X++,b(c)&&E(a,da)&&(c=a.style.width+q(a,da)),b(d)&&E(a,fa)&&(d=a.style.height+q(a,fa));var f=b(c)?ua:va,g=b(d)?ua:va;T(a,c,d,e,f,g,!0,"initial")&&P(a,a.layout.direction)}var V,W=!1,X=0,Y="left",Z="top",$="right",_="bottom",aa="inherit",ba="ltr",ca="rtl",da="row",ea="row-reverse",fa="column",ga="column-reverse",ha="flex-start",ia="center",ja="flex-end",ka="space-between",la="space-around",ma="flex-start",na="center",oa="flex-end",pa="stretch",qa="relative",ra="absolute",sa="visible",ta="hidden",ua="undefined",va="exactly",wa="at-most",xa={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},ya={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},za={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},Aa={row:"width","row-reverse":"width",column:"height","column-reverse":"height"},Ba={row:"measuredWidth","row-reverse":"measuredWidth",column:"measuredHeight","column-reverse":"measuredHeight"};return{layoutNodeImpl:R,computeLayout:U,fillNodes:a,canUseCachedMeasurement:S}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); +!function(a,b){"function"==typeof define&&define.amd?define([],b):"object"==typeof exports?module.exports=b():a.computeLayout=b()}(this,function(){var a=function(){function a(b){if(b.layout&&!b.isDirty||(b.layout={width:void 0,height:void 0,top:0,left:0,right:0,bottom:0}),b.style||(b.style={}),b.children||(b.children=[]),b.style.measure&&b.children&&b.children.length)throw new Error("Using custom measure function is supported only for leaf nodes.");return b.children.forEach(a),b}function b(a){return void 0===a||Number.isNaN(a)}function c(a){return a===da||a===ea}function d(a){return a===fa||a===ga}function e(a){return void 0===a.style.flex?0:a.style.flex}function f(a){return W?!0:e(a)<=0}function g(a){return e(a)>0?e(a):0}function h(a){if(W){if(0!==e(a))return 1}else if(e(a)<0)return 1;return 0}function i(a,b){if(void 0!==a.style.marginStart&&c(b))return a.style.marginStart;var d=null;switch(b){case"row":d=a.style.marginLeft;break;case"row-reverse":d=a.style.marginRight;break;case"column":d=a.style.marginTop;break;case"column-reverse":d=a.style.marginBottom}return void 0!==d?d:void 0!==a.style.margin?a.style.margin:0}function j(a,b){if(void 0!==a.style.marginEnd&&c(b))return a.style.marginEnd;var d=null;switch(b){case"row":d=a.style.marginRight;break;case"row-reverse":d=a.style.marginLeft;break;case"column":d=a.style.marginBottom;break;case"column-reverse":d=a.style.marginTop}return null!=d?d:void 0!==a.style.margin?a.style.margin:0}function k(a,b){if(void 0!==a.style.paddingStart&&a.style.paddingStart>=0&&c(b))return a.style.paddingStart;var d=null;switch(b){case"row":d=a.style.paddingLeft;break;case"row-reverse":d=a.style.paddingRight;break;case"column":d=a.style.paddingTop;break;case"column-reverse":d=a.style.paddingBottom}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function l(a,b){if(void 0!==a.style.paddingEnd&&a.style.paddingEnd>=0&&c(b))return a.style.paddingEnd;var d=null;switch(b){case"row":d=a.style.paddingRight;break;case"row-reverse":d=a.style.paddingLeft;break;case"column":d=a.style.paddingBottom;break;case"column-reverse":d=a.style.paddingTop}return null!=d&&d>=0?d:void 0!==a.style.padding&&a.style.padding>=0?a.style.padding:0}function m(a,b){if(void 0!==a.style.borderStartWidth&&a.style.borderStartWidth>=0&&c(b))return a.style.borderStartWidth;var d=null;switch(b){case"row":d=a.style.borderLeftWidth;break;case"row-reverse":d=a.style.borderRightWidth;break;case"column":d=a.style.borderTopWidth;break;case"column-reverse":d=a.style.borderBottomWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function n(a,b){if(void 0!==a.style.borderEndWidth&&a.style.borderEndWidth>=0&&c(b))return a.style.borderEndWidth;var d=null;switch(b){case"row":d=a.style.borderRightWidth;break;case"row-reverse":d=a.style.borderLeftWidth;break;case"column":d=a.style.borderBottomWidth;break;case"column-reverse":d=a.style.borderTopWidth}return null!=d&&d>=0?d:void 0!==a.style.borderWidth&&a.style.borderWidth>=0?a.style.borderWidth:0}function o(a,b){return k(a,b)+m(a,b)}function p(a,b){return l(a,b)+n(a,b)}function q(a,b){return i(a,b)+j(a,b)}function r(a,b){return o(a,b)+p(a,b)}function s(a){return a.style.justifyContent?a.style.justifyContent:"flex-start"}function t(a){return a.style.alignContent?a.style.alignContent:"flex-start"}function u(a,b){return b.style.alignSelf?b.style.alignSelf:a.style.alignItems?a.style.alignItems:"stretch"}function v(a,b){if(b===ca){if(a===da)return ea;if(a===ea)return da}return a}function w(a,b){var c;return c=a.style.direction?a.style.direction:aa,c===aa&&(c=void 0===b?ba:b),c}function x(a){return a.style.flexDirection?a.style.flexDirection:fa}function y(a,b){return d(a)?v(da,b):fa}function z(a){return a.style.position?a.style.position:qa}function A(a){return a.style.overflow?a.style.overflow:sa}function B(a){return z(a)===qa&&void 0!==a.style.flex&&0!==a.style.flex}function C(a){return"wrap"===a.style.flexWrap}function D(a,b){return a.layout[Ba[b]]+q(a,b)}function E(a,b){return void 0!==a.style[Aa[b]]&&a.style[Aa[b]]>=0}function F(a,b){return void 0!==a.layout[Ba[b]]&&a.layout[Ba[b]]>=0}function G(a,b){return void 0!==a.style[b]}function H(a){return void 0!==a.style.measure}function I(a,b){return void 0!==a.style[b]?a.style[b]:0}function J(a,b,c){var d={row:a.style.minWidth,"row-reverse":a.style.minWidth,column:a.style.minHeight,"column-reverse":a.style.minHeight}[b],e={row:a.style.maxWidth,"row-reverse":a.style.maxWidth,column:a.style.maxHeight,"column-reverse":a.style.maxHeight}[b],f=c;return void 0!==e&&e>=0&&f>e&&(f=e),void 0!==d&&d>=0&&d>f&&(f=d),f}function K(a,b){return b>a?a:b}function L(a,b){return a>b?a:b}function M(a,b,c){return L(J(a,b,c),r(a,b))}function N(a,b,c){var d=z(b)===ra?0:b.layout[Ba[c]];b.layout[ya[c]]=a.layout[Ba[c]]-d-b.layout[za[c]]}function O(a,b){return void 0!==a.style[xa[b]]?I(a,xa[b]):-I(a,ya[b])}function P(a,b){var c=v(x(a),b),d=y(c,b);a.layout[xa[c]]=i(a,c)+O(a,c),a.layout[ya[c]]=j(a,c)+O(a,c),a.layout[xa[d]]=i(a,d)+O(a,d),a.layout[ya[d]]=j(a,d)+O(a,d)}function Q(a,b){if(!a)throw new Error(b)}function R(a,d,e,k,l,O,R){Q(b(d)?l===ua:!0,"availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED"),Q(b(e)?O===ua:!0,"availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED");var S=r(a,da),U=r(a,fa),W=q(a,da),X=q(a,fa),aa=w(a,k);if(a.layout.direction=aa,H(a)){var ba=d-W-S,ca=e-X-U;if(l===va&&O===va)a.layout.measuredWidth=M(a,da,d-W),a.layout.measuredHeight=M(a,fa,e-X);else if(0>=ba||0>=ca)a.layout.measuredWidth=M(a,da,0),a.layout.measuredHeight=M(a,fa,0);else{var sa=a.style.measure(ba,l,ca,O);a.layout.measuredWidth=M(a,da,l===ua||l===wa?sa.width+S:d-W),a.layout.measuredHeight=M(a,fa,O===ua||O===wa?sa.height+U:e-X)}}else{var Aa=a.children.length;if(0===Aa)return a.layout.measuredWidth=M(a,da,l===ua||l===wa?S:d-W),void(a.layout.measuredHeight=M(a,fa,O===ua||O===wa?U:e-X));if(!R){if(l===wa&&0>=d&&O===wa&&0>=e)return a.layout.measuredWidth=M(a,da,0),void(a.layout.measuredHeight=M(a,fa,0));if(l===wa&&0>=d)return a.layout.measuredWidth=M(a,da,0),void(a.layout.measuredHeight=M(a,fa,b(e)?0:e-X));if(O===wa&&0>=e)return a.layout.measuredWidth=M(a,da,b(d)?0:d-W),void(a.layout.measuredHeight=M(a,fa,0));if(l===va&&O===va)return a.layout.measuredWidth=M(a,da,d-W),void(a.layout.measuredHeight=M(a,fa,e-X))}var Ca,Da,Ea,Fa,Ga,Ha,Ia=v(x(a),aa),Ja=y(Ia,aa),Ka=c(Ia),La=s(a),Ma=C(a),Na=void 0,Oa=void 0,Pa=o(a,Ia),Qa=p(a,Ia),Ra=o(a,Ja),Sa=r(a,Ia),Ta=r(a,Ja),Ua=Ka?l:O,Va=Ka?O:l,Wa=d-W-S,Xa=e-X-U,Ya=Ka?Wa:Xa,Za=Ka?Xa:Wa;for(Da=0;Aa>Da;Da++){if(Ca=a.children[Da],R){var $a=w(Ca,aa);P(Ca,$a)}z(Ca)===ra?(void 0===Na&&(Na=Ca),void 0!==Oa&&(Oa.nextChild=Ca),Oa=Ca,Ca.nextChild=void 0):Ka&&E(Ca,da)?Ca.layout.flexBasis=L(Ca.style.width,r(Ca,da)):!Ka&&E(Ca,fa)?Ca.layout.flexBasis=L(Ca.style.height,r(Ca,fa)):f(Ca)||b(Ya)?(Ea=V,Fa=V,Ga=ua,Ha=ua,E(Ca,da)&&(Ea=Ca.style.width+q(Ca,da),Ga=va),E(Ca,fa)&&(Fa=Ca.style.height+q(Ca,fa),Ha=va),Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=wa),A(a)===ta&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=wa),T(Ca,Ea,Fa,aa,Ga,Ha,!1,"measure"),Ca.layout.flexBasis=L(Ka?Ca.layout.measuredWidth:Ca.layout.measuredHeight,r(Ca,Ia))):Ca.layout.flexBasis=L(0,r(Ca,Ia))}for(var _a=0,ab=0,bb=0,cb=0,db=0;Aa>ab;){var eb=0,fb=0,gb=0,hb=0;Da=_a;for(var ib=void 0,jb=void 0;Aa>Da;){if(Ca=a.children[Da],Ca.lineIndex=bb,z(Ca)!==ra){var kb=Ca.layout.flexBasis+q(Ca,Ia);if(fb+kb>Ya&&Ma&&eb>0)break;fb+=kb,eb++,B(Ca)&&(gb+=g(Ca),hb+=h(Ca)*Ca.layout.flexBasis),void 0===ib&&(ib=Ca),void 0!==jb&&(jb.nextChild=Ca),jb=Ca,Ca.nextChild=void 0}Da++,ab++}var lb=!R&&Va===va,mb=0,nb=0,ob=0;b(Ya)?0>fb&&(ob=-fb):ob=Ya-fb;var pb=ob,qb=0;if(!lb){var rb,sb,tb,ub,vb,wb=0,xb=0;for(jb=ib;void 0!==jb;)rb=jb.layout.flexBasis,0>ob?(sb=h(jb)*rb,0!==sb&&(ub=rb+ob/hb*sb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,wb-=sb))):ob>0&&(tb=g(jb),0!==tb&&(ub=rb+ob/gb*tb,vb=M(jb,Ia,ub),ub!==vb&&(qb-=vb-rb,xb-=tb))),jb=jb.nextChild;for(hb+=wb,gb+=xb,ob+=qb,qb=0,jb=ib;void 0!==jb;){rb=jb.layout.flexBasis;var yb=rb;0>ob?(sb=h(jb)*rb,0!==sb&&(yb=M(jb,Ia,rb+ob/hb*sb))):ob>0&&(tb=g(jb),0!==tb&&(yb=M(jb,Ia,rb+ob/gb*tb))),qb-=yb-rb,Ka?(Ea=yb+q(jb,da),Ga=va,E(jb,fa)?(Fa=jb.style.height+q(jb,fa),Ha=va):(Fa=Za,Ha=b(Fa)?ua:wa)):(Fa=yb+q(jb,fa),Ha=va,E(jb,da)?(Ea=jb.style.width+q(jb,da),Ga=va):(Ea=Za,Ga=b(Ea)?ua:wa));var zb=!E(jb,Ja)&&u(a,jb)===pa;T(jb,Ea,Fa,aa,Ga,Ha,R&&!zb,"flex"),jb=jb.nextChild}}ob=pb+qb,Ua===wa&&(ob=0),La!==ha&&(La===ia?mb=ob/2:La===ja?mb=ob:La===ka?(ob=L(ob,0),nb=eb>1?ob/(eb-1):0):La===la&&(nb=ob/eb,mb=nb/2));var Ab=Pa+mb,Bb=0;for(Da=_a;ab>Da;++Da)Ca=a.children[Da],z(Ca)===ra&&G(Ca,xa[Ia])?R&&(Ca.layout[za[Ia]]=I(Ca,xa[Ia])+m(a,Ia)+i(Ca,Ia)):(R&&(Ca.layout[za[Ia]]+=Ab),z(Ca)===qa&&(lb?(Ab+=nb+q(Ca,Ia)+Ca.layout.flexBasis,Bb=Za):(Ab+=nb+D(Ca,Ia),Bb=L(Bb,D(Ca,Ja)))));Ab+=Qa;var Cb=Za;if(Va!==ua&&Va!==wa||(Cb=M(a,Ja,Bb+Ta)-Ta,Va===wa&&(Cb=K(Cb,Za))),Ma||Va!==va||(Bb=Za),Bb=M(a,Ja,Bb+Ta)-Ta,R)for(Da=_a;ab>Da;++Da)if(Ca=a.children[Da],z(Ca)===ra)G(Ca,xa[Ja])?Ca.layout[za[Ja]]=I(Ca,xa[Ja])+m(a,Ja)+i(Ca,Ja):Ca.layout[za[Ja]]=Ra+i(Ca,Ja);else{var Db=Ra,Eb=u(a,Ca);if(Eb===pa){Ea=Ca.layout.measuredWidth+q(Ca,da),Fa=Ca.layout.measuredHeight+q(Ca,fa);var Fb=!1;Ka?(Fb=E(Ca,fa),Fa=Bb):(Fb=E(Ca,da),Ea=Bb),Fb||(Ga=b(Ea)?ua:va,Ha=b(Fa)?ua:va,T(Ca,Ea,Fa,aa,Ga,Ha,!0,"stretch"))}else if(Eb!==ma){var Gb=Cb-D(Ca,Ja);Db+=Eb===na?Gb/2:Gb}Ca.layout[za[Ja]]+=cb+Db}cb+=Bb,db=L(db,Ab),bb++,_a=ab,ab=_a}if(bb>1&&R&&!b(Za)){var Hb=Za-cb,Ib=0,Jb=Ra,Kb=t(a);Kb===oa?Jb+=Hb:Kb===na?Jb+=Hb/2:Kb===pa&&Za>cb&&(Ib=Hb/bb);var Lb=0;for(Da=0;bb>Da;++Da){var Mb,Nb=Lb,Ob=0;for(Mb=Nb;Aa>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===qa){if(Ca.lineIndex!==Da)break;F(Ca,Ja)&&(Ob=L(Ob,Ca.layout[Ba[Ja]]+q(Ca,Ja)))}if(Lb=Mb,Ob+=Ib,R)for(Mb=Nb;Lb>Mb;++Mb)if(Ca=a.children[Mb],z(Ca)===qa){var Pb=u(a,Ca);Pb===ma?Ca.layout[za[Ja]]=Jb+i(Ca,Ja):Pb===oa?Ca.layout[za[Ja]]=Jb+Ob-j(Ca,Ja)-Ca.layout[Ba[Ja]]:Pb===na?(Fa=Ca.layout[Ba[Ja]],Ca.layout[za[Ja]]=Jb+(Ob-Fa)/2):Pb===pa&&(Ca.layout[za[Ja]]=Jb+i(Ca,Ja))}Jb+=Ob}}if(a.layout.measuredWidth=M(a,da,d-W),a.layout.measuredHeight=M(a,fa,e-X),Ua===ua?a.layout[Ba[Ia]]=M(a,Ia,db):Ua===wa&&(a.layout[Ba[Ia]]=L(K(Ya+Sa,J(a,Ia,db)),Sa)),Va===ua?a.layout[Ba[Ja]]=M(a,Ja,cb+Ta):Va===wa&&(a.layout[Ba[Ja]]=L(K(Za+Ta,J(a,Ja,cb+Ta)),Ta)),R){var Qb=!1,Rb=!1;if(Ia!==ea&&Ia!==ga||(Qb=!0),Ja!==ea&&Ja!==ga||(Rb=!0),Qb||Rb)for(Da=0;Aa>Da;++Da)Ca=a.children[Da],Qb&&N(a,Ca,Ia),Rb&&N(a,Ca,Ja)}for(Oa=Na;void 0!==Oa;)R&&(Ea=V,Fa=V,E(Oa,da)?Ea=Oa.style.width+q(Oa,da):G(Oa,Y)&&G(Oa,$)&&(Ea=a.layout.measuredWidth-(m(a,da)+n(a,da))-(Oa.style[Y]+Oa.style[$]),Ea=M(Oa,da,Ea)),E(Oa,fa)?Fa=Oa.style.height+q(Oa,fa):G(Oa,Z)&&G(Oa,_)&&(Fa=a.layout.measuredHeight-(m(a,fa)+n(a,fa))-(Oa.style[Z]+Oa.style[_]),Fa=M(Oa,fa,Fa)),(b(Ea)||b(Fa))&&(Ga=b(Ea)?ua:va,Ha=b(Fa)?ua:va,Ka||!b(Ea)||b(Wa)||(Ea=Wa,Ga=wa),A(a)===ta&&Ka&&b(Fa)&&!b(Xa)&&(Fa=Xa,Ha=wa),T(Oa,Ea,Fa,aa,Ga,Ha,!1,"abs-measure"),Ea=Oa.layout.measuredWidth+q(Oa,da),Fa=Oa.layout.measuredHeight+q(Oa,fa)),T(Oa,Ea,Fa,aa,va,va,!0,"abs-layout"),G(Oa,ya[da])&&!G(Oa,xa[da])&&(Oa.layout[xa[da]]=a.layout[Ba[da]]-Oa.layout[Ba[da]]-I(Oa,ya[da])),G(Oa,ya[fa])&&!G(Oa,xa[fa])&&(Oa.layout[xa[fa]]=a.layout[Ba[fa]]-Oa.layout[Ba[fa]]-I(Oa,ya[fa]))),Oa=Oa.nextChild}}function S(a,b,c,d,e,f,g){return g.availableWidth===a&&g.availableHeight===b&&g.widthMeasureMode===e&&g.heightMeasureMode===f?!0:g.availableWidth===a&&g.widthMeasureMode===e&&f===va&&b-d===g.computedHeight?!0:g.availableHeight===b&&g.heightMeasureMode===f&&e===va&&a-c===g.computedWidth}function T(a,b,c,d,e,f,g,h){var i=a.layout,j=a.isDirty&&i.generationCount!==X||i.lastParentDirection!==d;j&&(void 0!==i.cachedMeasurements&&(i.cachedMeasurements=[]),void 0!==i.cachedLayout&&(i.cachedLayout.widthMeasureMode=void 0,i.cachedLayout.heightMeasureMode=void 0));var k,l,m;if(H(a)){var n=q(a,da),o=q(a,fa);if(i.cachedLayout&&S(b,c,n,o,e,f,i.cachedLayout))m=i.cachedLayout;else if(i.cachedMeasurements)for(k=0,l=i.cachedMeasurements.length;l>k;k++)if(S(b,c,n,o,e,f,i.cachedMeasurements[k])){m=i.cachedMeasurements[k];break}}else if(g)i.cachedLayout&&i.cachedLayout.availableWidth===b&&i.cachedLayout.availableHeight===c&&i.cachedLayout.widthMeasureMode===e&&i.cachedLayout.heightMeasureMode===f&&(m=i.cachedLayout);else if(i.cachedMeasurements)for(k=0,l=i.cachedMeasurements.length;l>k;k++)if(i.cachedMeasurements[k].availableWidth===b&&i.cachedMeasurements[k].availableHeight===c&&i.cachedMeasurements[k].widthMeasureMode===e&&i.cachedMeasurements[k].heightMeasureMode===f){m=i.cachedMeasurements[k];break}if(j||void 0===m){if(R(a,b,c,d,e,f,g),i.lastParentDirection=d,void 0===m){var p;g?(void 0===i.cachedLayout&&(i.cachedLayout={}),p=i.cachedLayout):(void 0===i.cachedMeasurements&&(i.cachedMeasurements=[]),p={},i.cachedMeasurements.push(p)),p.availableWidth=b,p.availableHeight=c,p.widthMeasureMode=e,p.heightMeasureMode=f,p.computedWidth=i.measuredWidth,p.computedHeight=i.measuredHeight}}else i.measureWidth=m.computedWidth,i.measureHeight=m.computedHeight;return g&&(a.layout.width=a.layout.measuredWidth,a.layout.height=a.layout.measuredHeight,i.shouldUpdate=!0),i.generationCount=X,j||void 0===m}function U(a,c,d,e){X++,b(c)&&E(a,da)&&(c=a.style.width+q(a,da)),b(d)&&E(a,fa)&&(d=a.style.height+q(a,fa));var f=b(c)?ua:va,g=b(d)?ua:va;T(a,c,d,e,f,g,!0,"initial")&&P(a,a.layout.direction)}var V,W=!1,X=0,Y="left",Z="top",$="right",_="bottom",aa="inherit",ba="ltr",ca="rtl",da="row",ea="row-reverse",fa="column",ga="column-reverse",ha="flex-start",ia="center",ja="flex-end",ka="space-between",la="space-around",ma="flex-start",na="center",oa="flex-end",pa="stretch",qa="relative",ra="absolute",sa="visible",ta="hidden",ua="undefined",va="exactly",wa="at-most",xa={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},ya={row:"right","row-reverse":"left",column:"bottom","column-reverse":"top"},za={row:"left","row-reverse":"right",column:"top","column-reverse":"bottom"},Aa={row:"width","row-reverse":"width",column:"height","column-reverse":"height"},Ba={row:"measuredWidth","row-reverse":"measuredWidth",column:"measuredHeight","column-reverse":"measuredHeight"};return{layoutNodeImpl:R,computeLayout:U,fillNodes:a,canUseCachedMeasurement:S}}();return"object"==typeof exports&&(module.exports=a),function(b){a.fillNodes(b),a.computeLayout(b)}}); //# sourceMappingURL=css-layout.min.js.map \ No newline at end of file diff --git a/dist/css-layout.min.js.map b/dist/css-layout.min.js.map index 52b7586f..e73d0ca2 100644 --- a/dist/css-layout.min.js.map +++ b/dist/css-layout.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","Number","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getFlex","flex","isFlexBasisAuto","POSITIVE_FLEX_IS_AUTO","getFlexGrowFactor","getFlexShrinkFactor","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","CSS_POSITION_RELATIVE","getOverflow","overflow","CSS_OVERFLOW_VISIBLE","isFlex","isFlexWrap","flexWrap","getDimWithMargin","measuredDim","isStyleDimDefined","dim","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxisWithinMinAndMax","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fminf","a","b","fmaxf","boundAxis","setTrailingPosition","size","CSS_POSITION_ABSOLUTE","trailing","getRelativePosition","leading","setPosition","mainAxis","crossAxis","assert","condition","message","layoutNodeImpl","availableWidth","availableHeight","widthMeasureMode","heightMeasureMode","performLayout","CSS_MEASURE_MODE_UNDEFINED","paddingAndBorderAxisRow","paddingAndBorderAxisColumn","marginAxisRow","marginAxisColumn","innerWidth","innerHeight","CSS_MEASURE_MODE_EXACTLY","measuredWidth","measuredHeight","measureDim","CSS_MEASURE_MODE_AT_MOST","childCount","i","childWidth","childHeight","childWidthMeasureMode","childHeightMeasureMode","isMainAxisRow","isNodeFlexWrap","firstAbsoluteChild","currentAbsoluteChild","leadingPaddingAndBorderMain","trailingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","measureModeMainDim","measureModeCrossDim","availableInnerWidth","availableInnerHeight","availableInnerMainDim","availableInnerCrossDim","childDirection","nextChild","flexBasis","CSS_UNDEFINED","CSS_OVERFLOW_HIDDEN","layoutNodeInternal","startOfLineIndex","endOfLineIndex","lineCount","totalLineCrossDim","maxLineMainDim","itemsOnLine","sizeConsumedOnCurrentLine","totalFlexGrowFactors","totalFlexShrinkScaledFactors","firstRelativeChild","currentRelativeChild","lineIndex","outerFlexBasis","canSkipFlex","leadingMainDim","betweenMainDim","remainingFreeSpace","originalRemainingFreeSpace","deltaFreeSpace","childFlexBasis","flexShrinkScaledFactor","flexGrowFactor","baseMainSize","boundMainSize","deltaFlexShrinkScaledFactors","deltaFlexGrowFactors","updatedMainSize","requiresStretchLayout","CSS_ALIGN_STRETCH","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","mainDim","crossDim","containerCrossAxis","leadingCrossDim","alignItem","isCrossSizeDefinite","CSS_ALIGN_FLEX_START","remainingCrossDim","CSS_ALIGN_CENTER","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","j","startIndex","lineHeight","alignContentAlignItem","needsMainTrailingPos","needsCrossTrailingPos","CSS_LEFT","CSS_RIGHT","CSS_TOP","CSS_BOTTOM","canUseCachedMeasurement","marginRow","marginColumn","cachedLayout","computedHeight","computedWidth","reason","needToVisitNode","generationCount","gCurrentGenerationCount","lastParentDirection","cachedMeasurements","len","cachedResults","newCacheEntry","push","measureWidth","measureHeight","shouldUpdate","layoutNode"],"mappings":"CAKC,SAASA,EAAMC,GACQ,kBAAXC,SAAyBA,OAAOC,IAEzCD,UAAWD,GACiB,gBAAZG,SAIhBC,OAAOD,QAAUH,IAGjBD,EAAKM,cAAgBL,KAEvBM,KAAM,WAUR,GAAID,GAAgB,WA6ElB,QAASE,GAAUC,GAoBjB,GAnBKA,EAAKC,SAAUD,EAAKE,UACvBF,EAAKC,QACHE,MAAOC,OACPC,OAAQD,OACRE,IAAK,EACLC,KAAM,EACNC,MAAO,EACPC,OAAQ,IAIPT,EAAKU,QACRV,EAAKU,UAGFV,EAAKW,WACRX,EAAKW,aAGHX,EAAKU,MAAME,SAAWZ,EAAKW,UAAYX,EAAKW,SAASE,OACvD,KAAM,IAAIC,OAAM,kEAIlB,OADAd,GAAKW,SAASI,QAAQhB,GACfC,EAGT,QAASgB,GAAYC,GACnB,MAAiBb,UAAVa,GAAuBC,OAAOC,MAAMF,GAG7C,QAASG,GAAeC,GACtB,MAAOA,KAAkBC,IAClBD,IAAkBE,GAG3B,QAASC,GAAkBH,GACzB,MAAOA,KAAkBI,IAClBJ,IAAkBK,GAG3B,QAASC,GAAQ3B,GACf,MAAwBI,UAApBJ,EAAKU,MAAMkB,KACN,EAEF5B,EAAKU,MAAMkB,KAGpB,QAASC,GAAgB7B,GACvB,MAAI8B,IAEK,EAGAH,EAAQ3B,IAAS,EAI5B,QAAS+B,GAAkB/B,GAEzB,MAAI2B,GAAQ3B,GAAQ,EACX2B,EAAQ3B,GAEV,EAGT,QAASgC,GAAoBhC,GAC3B,GAAI8B,GAEF,GAAsB,IAAlBH,EAAQ3B,GACV,MAAO,OAIT,IAAI2B,EAAQ3B,GAAQ,EAClB,MAAO,EAGX,OAAO,GAGT,QAASiC,GAAiBjC,EAAMkC,GAC9B,GAA+B9B,SAA3BJ,EAAKU,MAAMyB,aAA6Bf,EAAec,GACzD,MAAOlC,GAAKU,MAAMyB,WAGpB,IAAIlB,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM0B,UAAc,MACxD,KAAK,cAAkBnB,EAAQjB,EAAKU,MAAM2B,WAAc,MACxD,KAAK,SAAkBpB,EAAQjB,EAAKU,MAAM4B,SAAc,MACxD,KAAK,iBAAkBrB,EAAQjB,EAAKU,MAAM6B,aAG5C,MAAcnC,UAAVa,EACKA,EAGiBb,SAAtBJ,EAAKU,MAAM8B,OACNxC,EAAKU,MAAM8B,OAGb,EAGT,QAASC,GAAkBzC,EAAMkC,GAC/B,GAA6B9B,SAAzBJ,EAAKU,MAAMgC,WAA2BtB,EAAec,GACvD,MAAOlC,GAAKU,MAAMgC,SAGpB,IAAIzB,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM2B,WAAc,MACxD,KAAK,cAAkBpB,EAAQjB,EAAKU,MAAM0B,UAAc,MACxD,KAAK,SAAkBnB,EAAQjB,EAAKU,MAAM6B,YAAc,MACxD,KAAK,iBAAkBtB,EAAQjB,EAAKU,MAAM4B,UAG5C,MAAa,OAATrB,EACKA,EAGiBb,SAAtBJ,EAAKU,MAAM8B,OACNxC,EAAKU,MAAM8B,OAGb,EAGT,QAASG,GAAkB3C,EAAMkC,GAC/B,GAAgC9B,SAA5BJ,EAAKU,MAAMkC,cAA8B5C,EAAKU,MAAMkC,cAAgB,GACjExB,EAAec,GACpB,MAAOlC,GAAKU,MAAMkC,YAGpB,IAAI3B,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAMmC,WAAe,MACzD,KAAK,cAAkB5B,EAAQjB,EAAKU,MAAMoC,YAAe,MACzD,KAAK,SAAkB7B,EAAQjB,EAAKU,MAAMqC,UAAe,MACzD,KAAK,iBAAkB9B,EAAQjB,EAAKU,MAAMsC,cAG5C,MAAa,OAAT/B,GAAiBA,GAAS,EACrBA,EAGkBb,SAAvBJ,EAAKU,MAAMuC,SAAyBjD,EAAKU,MAAMuC,SAAW,EACrDjD,EAAKU,MAAMuC,QAGb,EAGT,QAASC,GAAmBlD,EAAMkC,GAChC,GAA8B9B,SAA1BJ,EAAKU,MAAMyC,YAA4BnD,EAAKU,MAAMyC,YAAc,GAC7D/B,EAAec,GACpB,MAAOlC,GAAKU,MAAMyC,UAGpB,IAAIlC,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAMoC,YAAe,MACzD,KAAK,cAAkB7B,EAAQjB,EAAKU,MAAMmC,WAAe,MACzD,KAAK,SAAkB5B,EAAQjB,EAAKU,MAAMsC,aAAe,MACzD,KAAK,iBAAkB/B,EAAQjB,EAAKU,MAAMqC,WAG5C,MAAa,OAAT9B,GAAiBA,GAAS,EACrBA,EAGkBb,SAAvBJ,EAAKU,MAAMuC,SAAyBjD,EAAKU,MAAMuC,SAAW,EACrDjD,EAAKU,MAAMuC,QAGb,EAGT,QAASG,GAAiBpD,EAAMkC,GAC9B,GAAoC9B,SAAhCJ,EAAKU,MAAM2C,kBAAkCrD,EAAKU,MAAM2C,kBAAoB,GACzEjC,EAAec,GACpB,MAAOlC,GAAKU,MAAM2C,gBAGpB,IAAIpC,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM4C,eAAmB,MAC7D,KAAK,cAAkBrC,EAAQjB,EAAKU,MAAM6C,gBAAmB,MAC7D,KAAK,SAAkBtC,EAAQjB,EAAKU,MAAM8C,cAAmB,MAC7D,KAAK,iBAAkBvC,EAAQjB,EAAKU,MAAM+C,kBAG5C,MAAa,OAATxC,GAAiBA,GAAS,EACrBA,EAGsBb,SAA3BJ,EAAKU,MAAMgD,aAA6B1D,EAAKU,MAAMgD,aAAe,EAC7D1D,EAAKU,MAAMgD,YAGb,EAGT,QAASC,GAAkB3D,EAAMkC,GAC/B,GAAkC9B,SAA9BJ,EAAKU,MAAMkD,gBAAgC5D,EAAKU,MAAMkD,gBAAkB,GACrExC,EAAec,GACpB,MAAOlC,GAAKU,MAAMkD,cAGpB,IAAI3C,GAAQ,IACZ,QAAQiB,GACN,IAAK,MAAkBjB,EAAQjB,EAAKU,MAAM6C,gBAAmB,MAC7D,KAAK,cAAkBtC,EAAQjB,EAAKU,MAAM4C,eAAmB,MAC7D,KAAK,SAAkBrC,EAAQjB,EAAKU,MAAM+C,iBAAmB,MAC7D,KAAK,iBAAkBxC,EAAQjB,EAAKU,MAAM8C,eAG5C,MAAa,OAATvC,GAAiBA,GAAS,EACrBA,EAGsBb,SAA3BJ,EAAKU,MAAMgD,aAA6B1D,EAAKU,MAAMgD,aAAe,EAC7D1D,EAAKU,MAAMgD,YAGb,EAGT,QAASG,GAA2B7D,EAAMkC,GACxC,MAAOS,GAAkB3C,EAAMkC,GAAQkB,EAAiBpD,EAAMkC,GAGhE,QAAS4B,GAA4B9D,EAAMkC,GACzC,MAAOgB,GAAmBlD,EAAMkC,GAAQyB,EAAkB3D,EAAMkC,GAGlE,QAAS6B,GAAc/D,EAAMkC,GAC3B,MAAOD,GAAiBjC,EAAMkC,GAAQO,EAAkBzC,EAAMkC,GAGhE,QAAS8B,GAAwBhE,EAAMkC,GACrC,MAAO2B,GAA2B7D,EAAMkC,GACpC4B,EAA4B9D,EAAMkC,GAGxC,QAAS+B,GAAkBjE,GACzB,MAAIA,GAAKU,MAAMwD,eACNlE,EAAKU,MAAMwD,eAEb,aAGT,QAASC,GAAgBnE,GACvB,MAAIA,GAAKU,MAAM0D,aACNpE,EAAKU,MAAM0D,aAEb,aAGT,QAASC,GAAarE,EAAMsE,GAC1B,MAAIA,GAAM5D,MAAM6D,UACPD,EAAM5D,MAAM6D,UAEjBvE,EAAKU,MAAM8D,WACNxE,EAAKU,MAAM8D,WAEb,UAGT,QAASC,GAAYvC,EAAMwC,GACzB,GAAIA,IAAcC,GAAmB,CACnC,GAAIzC,IAASZ,GACX,MAAOC,GACF,IAAIW,IAASX,GAClB,MAAOD,IAIX,MAAOY,GAGT,QAAS0C,GAAiB5E,EAAM6E,GAC9B,GAAIH,EAWJ,OATEA,GADE1E,EAAKU,MAAMgE,UACD1E,EAAKU,MAAMgE,UAEXI,GAGVJ,IAAcI,KAChBJ,EAAiCtE,SAApByE,EAAgCE,GAAoBF,GAG5DH,EAGT,QAASM,GAAiBhF,GACxB,MAAIA,GAAKU,MAAMW,cACNrB,EAAKU,MAAMW,cAEbI,GAGT,QAASwD,GAAsB5D,EAAeqD,GAC5C,MAAIlD,GAAkBH,GACboD,EAAYnD,GAAwBoD,GAEpCjD,GAIX,QAASyD,GAAgBlF,GACvB,MAAIA,GAAKU,MAAMyE,SACNnF,EAAKU,MAAMyE,SAEbC,GAGT,QAASC,GAAYrF,GACnB,MAAIA,GAAKU,MAAM4E,SACNtF,EAAKU,MAAM4E,SAEbC,GAGT,QAASC,GAAOxF,GACd,MACEkF,GAAgBlF,KAAUoF,IACNhF,SAApBJ,EAAKU,MAAMkB,MAA0C,IAApB5B,EAAKU,MAAMkB,KAIhD,QAAS6D,GAAWzF,GAClB,MAA+B,SAAxBA,EAAKU,MAAMgF,SAGpB,QAASC,GAAiB3F,EAAMkC,GAC9B,MAAOlC,GAAKC,OAAO2F,GAAY1D,IAAS6B,EAAc/D,EAAMkC,GAG9D,QAAS2D,GAAkB7F,EAAMkC,GAC/B,MAAiC9B,UAA1BJ,EAAKU,MAAMoF,GAAI5D,KAAwBlC,EAAKU,MAAMoF,GAAI5D,KAAU,EAGzE,QAAS6D,GAAmB/F,EAAMkC,GAChC,MAA0C9B,UAAnCJ,EAAKC,OAAO2F,GAAY1D,KAAwBlC,EAAKC,OAAO2F,GAAY1D,KAAU,EAG3F,QAAS8D,GAAahG,EAAMiG,GAC1B,MAA2B7F,UAApBJ,EAAKU,MAAMuF,GAGpB,QAASC,GAAiBlG,GACxB,MAA8BI,UAAvBJ,EAAKU,MAAME,QAGpB,QAASuF,GAAYnG,EAAMiG,GACzB,MAAwB7F,UAApBJ,EAAKU,MAAMuF,GACNjG,EAAKU,MAAMuF,GAEb,EAGT,QAASG,GAAyBpG,EAAMkC,EAAMjB,GAC5C,GAAIoF,IACFC,IAAOtG,EAAKU,MAAM6F,SAClBC,cAAexG,EAAKU,MAAM6F,SAC1BE,OAAUzG,EAAKU,MAAMgG,UACrBC,iBAAkB3G,EAAKU,MAAMgG,WAC7BxE,GAEE0E,GACFN,IAAOtG,EAAKU,MAAMmG,SAClBL,cAAexG,EAAKU,MAAMmG,SAC1BJ,OAAUzG,EAAKU,MAAMoG,UACrBH,iBAAkB3G,EAAKU,MAAMoG,WAC7B5E,GAEE6E,EAAa9F,CAOjB,OANYb,UAARwG,GAAqBA,GAAO,GAAKG,EAAaH,IAChDG,EAAaH,GAEHxG,SAARiG,GAAqBA,GAAO,GAAkBA,EAAbU,IACnCA,EAAaV,GAERU,EAGT,QAASC,GAAMC,EAAGC,GAChB,MAAQA,GAAJD,EACKA,EAEFC,EAGT,QAASC,GAAMF,EAAGC,GAChB,MAAID,GAAIC,EACCD,EAEFC,EAKT,QAASE,GAAUpH,EAAMkC,EAAMjB,GAC7B,MAAOkG,GAAMf,EAAyBpG,EAAMkC,EAAMjB,GAAQ+C,EAAwBhE,EAAMkC,IAG1F,QAASmF,GAAoBrH,EAAMsE,EAAOpC,GACxC,GAAIoF,GAAQpC,EAAgBZ,KAAWiD,GACrC,EACAjD,EAAMrE,OAAO2F,GAAY1D,GAC3BoC,GAAMrE,OAAOuH,GAAStF,IAASlC,EAAKC,OAAO2F,GAAY1D,IAASoF,EAAOhD,EAAMrE,OAAOgG,GAAI/D,IAK1F,QAASuF,GAAoBzH,EAAMkC,GACjC,MAAkC9B,UAA9BJ,EAAKU,MAAMgH,GAAQxF,IACdiE,EAAYnG,EAAM0H,GAAQxF,KAE3BiE,EAAYnG,EAAMwH,GAAStF,IAGrC,QAASyF,GAAY3H,EAAM0E,GACzB,GAAIkD,GAAWnD,EAAYO,EAAiBhF,GAAO0E,GAC/CmD,EAAY5C,EAAsB2C,EAAUlD,EAEhD1E,GAAKC,OAAOyH,GAAQE,IAAa3F,EAAiBjC,EAAM4H,GACtDH,EAAoBzH,EAAM4H,GAC5B5H,EAAKC,OAAOuH,GAASI,IAAanF,EAAkBzC,EAAM4H,GACxDH,EAAoBzH,EAAM4H,GAC5B5H,EAAKC,OAAOyH,GAAQG,IAAc5F,EAAiBjC,EAAM6H,GACvDJ,EAAoBzH,EAAM6H,GAC5B7H,EAAKC,OAAOuH,GAASK,IAAcpF,EAAkBzC,EAAM6H,GACzDJ,EAAoBzH,EAAM6H,GAG9B,QAASC,GAAOC,EAAWC,GACzB,IAAKD,EACH,KAAM,IAAIjH,OAAMkH,GA+EpB,QAASC,GAAejI,EAAMkI,EAAgBC,EAAoCtD,EAAiBuD,EAAkBC,EAAmBC,GACtIR,EAAO9G,EAAYkH,GAAkBE,IAAqBG,IAA6B,EAAM,uFAC7FT,EAAO9G,EAAYmH,GAAmBE,IAAsBE,IAA6B,EAAM,wFAE/F,IAAaC,GAA0BxE,EAAwBhE,EAAMsB,IACxDmH,EAA6BzE,EAAwBhE,EAAMyB,IAC3DiH,EAAgB3E,EAAc/D,EAAMsB,IACpCqH,EAAmB5E,EAAc/D,EAAMyB,IAG7BiD,GAAYE,EAAiB5E,EAAM6E,EAI1D,IAHA7E,EAAKC,OAAOyE,UAAYA,GAGpBwB,EAAiBlG,GAArB,CACE,GAAa4I,IAAaV,EAAiBQ,EAAgBF,EAC9CK,GAAcV,EAAkBQ,EAAmBF,CAEhE,IAAIL,IAAqBU,IAA4BT,IAAsBS,GAGzE9I,EAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB4G,EAAiBQ,GACrF1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B0G,EAAkBQ,OACrF,IAAkB,GAAdC,GAGT5I,EAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB,GACpEtB,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B,OACnE,CAGL,GAAiBwH,IAAajJ,EAAKU,MAAME,QAGvCgI,GACAR,EACAS,GACAR,EAGFrI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GACzC8G,IAAqBG,IAA8BH,IAAqBc,GACvED,GAAW9I,MAAQqI,EACnBN,EAAiBQ,GACrB1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAC1C4G,IAAsBE,IAA8BF,IAAsBa,GACzED,GAAW5I,OAASoI,EACpBN,EAAkBQ,QAjC1B,CAyCA,GAAWQ,IAAanJ,EAAKW,SAASE,MACtC,IAAmB,IAAfsI,GASF,MARAnJ,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GACzC8G,IAAqBG,IAA8BH,IAAqBc,GACvEV,EACAN,EAAiBQ,QACrB1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAC1C4G,IAAsBE,IAA8BF,IAAsBa,GACzET,EACAN,EAAkBQ,GAMxB,KAAKL,EAAe,CAGlB,GAAIF,IAAqBc,IAA8C,GAAlBhB,GACjDG,IAAsBa,IAA+C,GAAnBf,EAGpD,MAFAnI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB,QACpEtB,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B,GAI1E,IAAI2G,IAAqBc,IAA8C,GAAlBhB,EAGnD,MAFAlI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB,QACpEtB,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2BT,EAAYmH,GAAmB,EAAKA,EAAkBQ,GAIhI,IAAIN,IAAsBa,IAA+C,GAAnBf,EAGpD,MAFAnI,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwBN,EAAYkH,GAAkB,EAAKA,EAAiBQ,QACxH1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B,GAK1E,IAAI2G,IAAqBU,IAA4BT,IAAsBS,GAGzE,MAFA9I,GAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB4G,EAAiBQ,QACrF1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B0G,EAAkBQ,IAM9F,GAyBmBrE,IACR8E,GACEC,GACAC,GACaC,GACAC,GA9BoB5B,GAAWnD,EAAYO,EAAiBhF,GAAO0E,IAC/CmD,GAAY5C,EAAsB2C,GAAUlD,IAC9E+E,GAAgBrI,EAAewG,IACtB1D,GAAiBD,EAAkBjE,GAC5C0J,GAAiBjE,EAAWzF,GAErB2J,GAAqBvJ,OACrBwJ,GAAuBxJ,OAE7ByJ,GAA8BhG,EAA2B7D,EAAM4H,IAC/DkC,GAA+BhG,EAA4B9D,EAAM4H,IACjEmC,GAA+BlG,EAA2B7D,EAAM6H,IAChEmC,GAA2BhG,EAAwBhE,EAAM4H,IACzDqC,GAA4BjG,EAAwBhE,EAAM6H,IAE7CqC,GAAqBT,GAAgBrB,EAAmBC,EACxD8B,GAAsBV,GAAgBpB,EAAoBD,EAGvEgC,GAAsBlC,EAAiBQ,EAAgBF,EACvD6B,GAAuBlC,EAAkBQ,EAAmBF,EAC5D6B,GAAwBb,GAAgBW,GAAsBC,GAC9DE,GAAyBd,GAAgBY,GAAuBD,EAS7E,KAAKhB,GAAI,EAAOD,GAAJC,GAAgBA,KAAK,CAG/B,GAFA9E,GAAQtE,EAAKW,SAASyI,IAElBd,EAAe,CAEjB,GAAuBkC,IAAiB5F,EAAiBN,GAAOI,GAChEiD,GAAYrD,GAAOkG,IAKjBtF,EAAgBZ,MAAWiD,IAIFnH,SAAvBuJ,KACFA,GAAqBrF,IAEMlE,SAAzBwJ,KACFA,GAAqBa,UAAYnG,IAEnCsF,GAAuBtF,GACvBA,GAAMmG,UAAYrK,QAGdqJ,IAAiB5D,EAAkBvB,GAAOhD,IAG5CgD,GAAMrE,OAAOyK,UAAYvD,EAAM7C,GAAM5D,MAAMP,MAAO6D,EAAwBM,GAAOhD,MACvEmI,IAAiB5D,EAAkBvB,GAAO7C,IAGpD6C,GAAMrE,OAAOyK,UAAYvD,EAAM7C,GAAM5D,MAAML,OAAQ2D,EAAwBM,GAAO7C,KACxEI,EAAgByC,KAAWtD,EAAYsJ,KAOjDjB,GAAasB,EACbrB,GAAcqB,EACdpB,GAAwBhB,GACxBiB,GAAyBjB,GAErB1C,EAAkBvB,GAAOhD,MAC3B+H,GAAa/E,GAAM5D,MAAMP,MAAQ4D,EAAcO,GAAOhD,IACtDiI,GAAwBT,IAEtBjD,EAAkBvB,GAAO7C,MAC3B6H,GAAchF,GAAM5D,MAAML,OAAS0D,EAAcO,GAAO7C,IACxD+H,GAAyBV,IAOtBW,KAAiBzI,EAAYqI,KAAgBrI,EAAYoJ,MAC5Df,GAAae,GACbb,GAAwBL,IAKtB7D,EAAYrF,KAAU4K,IACpBnB,IAAiBzI,EAAYsI,MAAiBtI,EAAYqJ,MAC5Df,GAAce,GACdb,GAAyBN,IAK7B2B,EAAmBvG,GAAO+E,GAAYC,GAAa5E,GAAW6E,GAAuBC,IAAwB,EAAO,WAEpHlF,GAAMrE,OAAOyK,UAAYvD,EAAMsC,GAAgBnF,GAAMrE,OAAO8I,cAAgBzE,GAAMrE,OAAO+I,eAAgBhF,EAAwBM,GAAOsD,MAvCxItD,GAAMrE,OAAOyK,UAAYvD,EAAM,EAAGnD,EAAwBM,GAAOsD,KA2DvE,IAZA,GAAWkD,IAAmB,EACnBC,GAAiB,EAGjBC,GAAY,EAGVC,GAAoB,EAGpBC,GAAiB,EAEN/B,GAAjB4B,IAA6B,CAIlC,GAAWI,IAAc,EAMZC,GAA4B,EAE5BC,GAAuB,EACvBC,GAA+B,CAE5ClC,IAAI0B,EAOJ,KAJA,GAAmBS,IAAqBnL,OACrBoL,GAAuBpL,OAG/B+I,GAAJC,IAAgB,CAIrB,GAHA9E,GAAQtE,EAAKW,SAASyI,IACtB9E,GAAMmH,UAAYT,GAEd9F,EAAgBZ,MAAWiD,GAAuB,CACpD,GAAamE,IAAiBpH,GAAMrE,OAAOyK,UAAY3G,EAAcO,GAAOsD,GAI5E,IAAIwD,GAA4BM,GAAiBpB,IAAyBZ,IAAkByB,GAAc,EACxG,KAGFC,KAA6BM,GAC7BP,KAEI3F,EAAOlB,MACT+G,IAAwBtJ,EAAkBuC,IAI1CgH,IAAgCtJ,EAAoBsC,IAASA,GAAMrE,OAAOyK,WAIjDtK,SAAvBmL,KACFA,GAAqBjH,IAEMlE,SAAzBoL,KACFA,GAAqBf,UAAYnG,IAEnCkH,GAAuBlH,GACvBA,GAAMmG,UAAYrK,OAGpBgJ,KACA2B,KAIF,GAAYY,KAAerD,GAAiB6B,KAAwBrB,GAKvD8C,GAAiB,EACjBC,GAAiB,EAMjBC,GAAqB,CAC7B9K,GAAYsJ,IAEsB,EAA5Bc,KAITU,IAAsBV,IALtBU,GAAqBxB,GAAwBc,EAQ/C,IAAaW,IAA6BD,GAC7BE,GAAiB,CAE9B,KAAKL,GAAa,CAChB,GAAaM,IACAC,GACAC,GACAC,GACAC,GAgBAC,GAA+B,EAC/BC,GAAuB,CAEpC,KADAf,GAAuBD,GACSnL,SAAzBoL,IACLS,GAAiBT,GAAqBvL,OAAOyK,UAEpB,EAArBoB,IACFI,GAAyBlK,EAAoBwJ,IAAwBS,GAGtC,IAA3BC,KACFE,GAAeH,GACbH,GAAqBR,GAA+BY,GACtDG,GAAgBjF,EAAUoE,GAAsB5D,GAAUwE,IACtDA,KAAiBC,KAInBL,IAAkBK,GAAgBJ,GAClCK,IAAgCJ,MAG3BJ,GAAqB,IAC9BK,GAAiBpK,EAAkByJ,IAGZ,IAAnBW,KACFC,GAAeH,GACbH,GAAqBT,GAAuBc,GAC9CE,GAAgBjF,EAAUoE,GAAsB5D,GAAUwE,IACtDA,KAAiBC,KAInBL,IAAkBK,GAAgBJ,GAClCM,IAAwBJ,MAK9BX,GAAuBA,GAAqBf,SAU9C,KAPAa,IAAgCgB,GAChCjB,IAAwBkB,GACxBT,IAAsBE,GAGtBA,GAAiB,EACjBR,GAAuBD,GACSnL,SAAzBoL,IAAoC,CACzCS,GAAiBT,GAAqBvL,OAAOyK,SAC7C,IAAa8B,IAAkBP,EAEN,GAArBH,IACFI,GAAyBlK,EAAoBwJ,IAAwBS,GAGtC,IAA3BC,KACFM,GAAkBpF,EAAUoE,GAAsB5D,GAAUqE,GAC1DH,GAAqBR,GAA+BY,MAE/CJ,GAAqB,IAC9BK,GAAiBpK,EAAkByJ,IAGZ,IAAnBW,KACFK,GAAkBpF,EAAUoE,GAAsB5D,GAAUqE,GAC1DH,GAAqBT,GAAuBc,MAIlDH,IAAkBQ,GAAkBP,GAEhCxC,IACFJ,GAAamD,GAAkBzI,EAAcyH,GAAsBlK,IACnEiI,GAAwBT,GAEnBjD,EAAkB2F,GAAsB/J,KAI3C6H,GAAckC,GAAqB9K,MAAML,OAAS0D,EAAcyH,GAAsB/J,IACtF+H,GAAyBV,KAJzBQ,GAAciB,GACdf,GAAyBxI,EAAYsI,IAAef,GAA6BW,MAMnFI,GAAckD,GAAkBzI,EAAcyH,GAAsB/J,IACpE+H,GAAyBV,GAEpBjD,EAAkB2F,GAAsBlK,KAI3C+H,GAAamC,GAAqB9K,MAAMP,MAAQ4D,EAAcyH,GAAsBlK,IACpFiI,GAAwBT,KAJxBO,GAAakB,GACbhB,GAAwBvI,EAAYqI,IAAcd,GAA6BW,IAOnF,IAAYuD,KAAyB5G,EAAkB2F,GAAsB3D,KAC3ExD,EAAarE,EAAMwL,MAA0BkB,EAG/C7B,GAAmBW,GAAsBnC,GAAYC,GAAa5E,GAAW6E,GAAuBC,GAAwBlB,IAAkBmE,GAAuB,QAErKjB,GAAuBA,GAAqBf,WAIhDqB,GAAqBC,GAA6BC,GAW9C9B,KAAuBhB,KACzB4C,GAAqB,GAKnB5H,KAAmByI,KACjBzI,KAAmB0I,GACrBhB,GAAiBE,GAAqB,EAC7B5H,KAAmB2I,GAC5BjB,GAAiBE,GACR5H,KAAmB4I,IAC5BhB,GAAqB3E,EAAM2E,GAAoB,GAE7CD,GADEV,GAAc,EACCW,IAAsBX,GAAc,GAEpC,GAEVjH,KAAmB6I,KAE5BlB,GAAiBC,GAAqBX,GACtCS,GAAiBC,GAAiB,GAItC,IAAamB,IAAUnD,GAA8B+B,GACxCqB,GAAW,CAExB,KAAK7D,GAAI0B,GAAsBC,GAAJ3B,KAAsBA,GAC/C9E,GAAQtE,EAAKW,SAASyI,IAElBlE,EAAgBZ,MAAWiD,IAC3BvB,EAAa1B,GAAOoD,GAAQE,KAC1BU,IAIFhE,GAAMrE,OAAOgG,GAAI2B,KAAazB,EAAY7B,GAAOoD,GAAQE,KACvDxE,EAAiBpD,EAAM4H,IACvB3F,EAAiBqC,GAAOsD,MAGxBU,IAGFhE,GAAMrE,OAAOgG,GAAI2B,MAAcoF,IAM7B9H,EAAgBZ,MAAWc,KACzBuG,IAGFqB,IAAWnB,GAAiB9H,EAAcO,GAAOsD,IAAYtD,GAAMrE,OAAOyK,UAC1EuC,GAAW1C,KAIXyC,IAAWnB,GAAiBlG,EAAiBrB,GAAOsD,IAIpDqF,GAAW9F,EAAM8F,GAAUtH,EAAiBrB,GAAOuD,OAM3DmF,KAAWlD,EAEX,IAAaoD,IAAqB3C,EAoBlC,IAnBIJ,KAAwB5B,IAA8B4B,KAAwBjB,KAEhFgE,GAAqB9F,EAAUpH,EAAM6H,GAAWoF,GAAWhD,IAA6BA,GAEpFE,KAAwBjB,KAC1BgE,GAAqBlG,EAAMkG,GAAoB3C,MAK9Cb,IAAkBS,KAAwBrB,KAC7CmE,GAAW1C,IAIb0C,GAAW7F,EAAUpH,EAAM6H,GAAWoF,GAAWhD,IAA6BA,GAI1E3B,EACF,IAAKc,GAAI0B,GAAsBC,GAAJ3B,KAAsBA,GAG/C,GAFA9E,GAAQtE,EAAKW,SAASyI,IAElBlE,EAAgBZ,MAAWiD,GAGzBvB,EAAa1B,GAAOoD,GAAQG,KAC9BvD,GAAMrE,OAAOgG,GAAI4B,KAAc1B,EAAY7B,GAAOoD,GAAQG,KACxDzE,EAAiBpD,EAAM6H,IACvB5F,EAAiBqC,GAAOuD,IAE1BvD,GAAMrE,OAAOgG,GAAI4B,KAAckC,GAC7B9H,EAAiBqC,GAAOuD,QAEvB,CACL,GAAasF,IAAkBpD,GAIZqD,GAAY/I,EAAarE,EAAMsE,GAIlD,IAAI8I,KAAcV,GAAmB,CACnCrD,GAAa/E,GAAMrE,OAAO8I,cAAgBhF,EAAcO,GAAOhD,IAC/DgI,GAAchF,GAAMrE,OAAO+I,eAAiBjF,EAAcO,GAAO7C,GACjE,IAAY4L,KAAsB,CAE9B5D,KACF4D,GAAsBxH,EAAkBvB,GAAO7C,IAC/C6H,GAAc2D,KAEdI,GAAsBxH,EAAkBvB,GAAOhD,IAC/C+H,GAAa4D,IAIVI,KACH9D,GAAwBvI,EAAYqI,IAAcd,GAA6BO,GAC/EU,GAAyBxI,EAAYsI,IAAef,GAA6BO,GACjF+B,EAAmBvG,GAAO+E,GAAYC,GAAa5E,GAAW6E,GAAuBC,IAAwB,EAAM,gBAEhH,IAAI4D,KAAcE,GAAsB,CAC7C,GAAaC,IAAoBL,GAAqBvH,EAAiBrB,GAAOuD,GAG5EsF,KADEC,KAAcI,GACGD,GAAoB,EAEpBA,GAKvBjJ,GAAMrE,OAAOgG,GAAI4B,MAAeoD,GAAoBkC,GAK1DlC,IAAqBgC,GACrB/B,GAAiB/D,EAAM+D,GAAgB8B,IAGvChC,KACAF,GAAmBC,GACnBA,GAAiBD,GAInB,GAAIE,GAAY,GAAK1C,IAAkBtH,EAAYuJ,IAAyB,CAC1E,GAAakD,IAA2BlD,GAAyBU,GAEpDyC,GAAe,EACfC,GAAc5D,GAER3F,GAAeD,EAAgBnE,EAC9CoE,MAAiBwJ,GACnBD,IAAeF,GACNrJ,KAAiBoJ,GAC1BG,IAAeF,GAA2B,EACjCrJ,KAAiBsI,IACtBnC,GAAyBU,KAC3ByC,GAAgBD,GAA2BzC,GAI/C,IAAW6C,IAAW,CACtB,KAAKzE,GAAI,EAAO4B,GAAJ5B,KAAiBA,GAAG,CAC9B,GACW0E,IADAC,GAAaF,GAIXG,GAAa,CAC1B,KAAKF,GAAIC,GAAgB5E,GAAJ2E,KAAkBA,GAErC,GADAxJ,GAAQtE,EAAKW,SAASmN,IAClB5I,EAAgBZ,MAAWc,GAA/B,CAGA,GAAId,GAAMmH,YAAcrC,GACtB,KAEErD,GAAmBzB,GAAOuD,MAC5BmG,GAAa7G,EAAM6G,GACjB1J,GAAMrE,OAAO2F,GAAYiC,KAAc9D,EAAcO,GAAOuD,MAMlE,GAHAgG,GAAWC,GACXE,IAAcN,GAEVpF,EACF,IAAKwF,GAAIC,GAAgBF,GAAJC,KAAgBA,GAEnC,GADAxJ,GAAQtE,EAAKW,SAASmN,IAClB5I,EAAgBZ,MAAWc,GAA/B,CAIA,GAAmB6I,IAAwB5J,EAAarE,EAAMsE,GAC1D2J,MAA0BX,GAC5BhJ,GAAMrE,OAAOgG,GAAI4B,KAAc8F,GAAc1L,EAAiBqC,GAAOuD,IAC5DoG,KAA0BL,GACnCtJ,GAAMrE,OAAOgG,GAAI4B,KAAc8F,GAAcK,GAAavL,EAAkB6B,GAAOuD,IAAavD,GAAMrE,OAAO2F,GAAYiC,KAChHoG,KAA0BT,IACnClE,GAAchF,GAAMrE,OAAO2F,GAAYiC,KACvCvD,GAAMrE,OAAOgG,GAAI4B,KAAc8F,IAAeK,GAAa1E,IAAe,GACjE2E,KAA0BvB,KACnCpI,GAAMrE,OAAOgG,GAAI4B,KAAc8F,GAAc1L,EAAiBqC,GAAOuD,KAO3E8F,IAAeK,IAiCnB,GA5BAhO,EAAKC,OAAO8I,cAAgB3B,EAAUpH,EAAMsB,GAAwB4G,EAAiBQ,GACrF1I,EAAKC,OAAO+I,eAAiB5B,EAAUpH,EAAMyB,GAA2B0G,EAAkBQ,GAItFuB,KAAuB3B,GAGzBvI,EAAKC,OAAO2F,GAAYgC,KAAaR,EAAUpH,EAAM4H,GAAUsD,IACtDhB,KAAuBhB,KAChClJ,EAAKC,OAAO2F,GAAYgC,KAAaT,EACnCH,EAAMsD,GAAwBN,GAC5B5D,EAAyBpG,EAAM4H,GAAUsD,KAC3ClB,KAGAG,KAAwB5B,GAG1BvI,EAAKC,OAAO2F,GAAYiC,KAAcT,EAAUpH,EAAM6H,GAAWoD,GAAoBhB,IAC5EE,KAAwBjB,KACjClJ,EAAKC,OAAO2F,GAAYiC,KAAcV,EACpCH,EAAMuD,GAAyBN,GAC7B7D,EAAyBpG,EAAM6H,GAAWoD,GAAoBhB,KAChEA,KAIA3B,EAAe,CACjB,GAAY4F,KAAuB,EACvBC,IAAwB,CAapC,IAXIvG,KAAarG,IACbqG,KAAalG,KACfwM,IAAuB,GAGrBrG,KAActG,IACdsG,KAAcnG,KAChByM,IAAwB,GAItBD,IAAwBC,GAC1B,IAAK/E,GAAI,EAAOD,GAAJC,KAAkBA,GAC5B9E,GAAQtE,EAAKW,SAASyI,IAElB8E,IACF7G,EAAoBrH,EAAMsE,GAAOsD,IAG/BuG,IACF9G,EAAoBrH,EAAMsE,GAAOuD,IAQzC,IADA+B,GAAuBD,GACSvJ,SAAzBwJ,IAGDtB,IAEFe,GAAasB,EACbrB,GAAcqB,EAEV9E,EAAkB+D,GAAsBtI,IAC1C+H,GAAaO,GAAqBlJ,MAAMP,MAAQ4D,EAAc6F,GAAsBtI,IAGhF0E,EAAa4D,GAAsBwE,IAAapI,EAAa4D,GAAsByE,KACrFhF,GAAarJ,EAAKC,OAAO8I,eACtB3F,EAAiBpD,EAAMsB,IAA0BqC,EAAkB3D,EAAMsB,MACzEsI,GAAqBlJ,MAAM0N,GAAYxE,GAAqBlJ,MAAM2N,IACrEhF,GAAajC,EAAUwC,GAAsBtI,GAAwB+H,KAIrExD,EAAkB+D,GAAsBnI,IAC1C6H,GAAcM,GAAqBlJ,MAAML,OAAS0D,EAAc6F,GAAsBnI,IAGlFuE,EAAa4D,GAAsB0E,IAAYtI,EAAa4D,GAAsB2E,KACpFjF,GAActJ,EAAKC,OAAO+I,gBACvB5F,EAAiBpD,EAAMyB,IAA6BkC,EAAkB3D,EAAMyB,MAC5EmI,GAAqBlJ,MAAM4N,GAAW1E,GAAqBlJ,MAAM6N,IACpEjF,GAAclC,EAAUwC,GAAsBnI,GAA2B6H,MAKzEtI,EAAYqI,KAAerI,EAAYsI,OACzCC,GAAwBvI,EAAYqI,IAAcd,GAA6BO,GAC/EU,GAAyBxI,EAAYsI,IAAef,GAA6BO,GAM5EW,KAAiBzI,EAAYqI,KAAgBrI,EAAYoJ,MAC5Df,GAAae,GACbb,GAAwBL,IAKtB7D,EAAYrF,KAAU4K,IACpBnB,IAAiBzI,EAAYsI,MAAiBtI,EAAYqJ,MAC5Df,GAAce,GACdb,GAAyBN,IAI7B2B,EAAmBjB,GAAsBP,GAAYC,GAAa5E,GAAW6E,GAAuBC,IAAwB,EAAO,eACnIH,GAAaO,GAAqB3J,OAAO8I,cAAgBhF,EAAc6F,GAAsBtI,IAC7FgI,GAAcM,GAAqB3J,OAAO+I,eAAiBjF,EAAc6F,GAAsBnI,KAGjGoJ,EAAmBjB,GAAsBP,GAAYC,GAAa5E,GAAWoE,GAA0BA,IAA0B,EAAM,cAEnI9C,EAAa4D,GAAsBpC,GAASlG,OAC3C0E,EAAa4D,GAAsBlC,GAAQpG,OAC9CsI,GAAqB3J,OAAOyH,GAAQpG,KAClCtB,EAAKC,OAAO2F,GAAYtE,KACxBsI,GAAqB3J,OAAO2F,GAAYtE,KACxC6E,EAAYyD,GAAsBpC,GAASlG,MAG3C0E,EAAa4D,GAAsBpC,GAAS/F,OAC3CuE,EAAa4D,GAAsBlC,GAAQjG,OAC9CmI,GAAqB3J,OAAOyH,GAAQjG,KAClCzB,EAAKC,OAAO2F,GAAYnE,KACxBmI,GAAqB3J,OAAO2F,GAAYnE,KACxC0E,EAAYyD,GAAsBpC,GAAS/F,OAIjDmI,GAAuBA,GAAqBa,WAIhD,QAAS+D,GAAwBtG,EAAgBC,EAC/CsG,EAAWC,EACXtG,EAAkBC,EAClBsG,GAGA,MAAIA,GAAazG,iBAAmBA,GAChCyG,EAAaxG,kBAAoBA,GACjCwG,EAAavG,mBAAqBA,GAClCuG,EAAatG,oBAAsBA,GAC9B,EAILsG,EAAazG,iBAAmBA,GAChCyG,EAAavG,mBAAqBA,GAClCC,IAAsBS,IACtBX,EAAkBuG,IAAiBC,EAAaC,gBAC3C,EAILD,EAAaxG,kBAAoBA,GACjCwG,EAAatG,oBAAsBA,GACnCD,IAAqBU,IACrBZ,EAAiBuG,IAAcE,EAAaE,cAelD,QAAShE,GAAmB7K,EAAMkI,EAAgBC,EAAiBtD,EAC/DuD,EAAkBC,EAAmBC,EAAewG,GACtD,GAAI7O,GAASD,EAAKC,OAEd8O,EAAmB/O,EAAKE,SAAWD,EAAO+O,kBAAoBC,GAChEhP,EAAOiP,sBAAwBrK,CAE7BkK,KAEgC3O,SAA9BH,EAAOkP,qBACTlP,EAAOkP,uBAEmB/O,SAAxBH,EAAO0O,eACT1O,EAAO0O,aAAavG,iBAAmBhI,OACvCH,EAAO0O,aAAatG,kBAAoBjI,QAI5C,IAAIgJ,GACAgG,EACAC,CASJ,IAAInJ,EAAiBlG,GAAO,CAC1B,GAAI0I,GAAgB3E,EAAc/D,EAAMsB,IACpCqH,EAAmB5E,EAAc/D,EAAMyB,GAG3C,IAAIxB,EAAO0O,cACPH,EAAwBtG,EAAgBC,EAAiBO,EAAeC,EACtEP,EAAkBC,EAAmBpI,EAAO0O,cAChDU,EAAgBpP,EAAO0O,iBAClB,IAAI1O,EAAOkP,mBAEhB,IAAK/F,EAAI,EAAGgG,EAAMnP,EAAOkP,mBAAmBtO,OAAYuO,EAAJhG,EAASA,IAC3D,GAAIoF,EAAwBtG,EAAgBC,EAAiBO,EAAeC,EACxEP,EAAkBC,EAAmBpI,EAAOkP,mBAAmB/F,IAAK,CACtEiG,EAAgBpP,EAAOkP,mBAAmB/F,EAC1C,YAID,IAAId,EACLrI,EAAO0O,cACP1O,EAAO0O,aAAazG,iBAAmBA,GACvCjI,EAAO0O,aAAaxG,kBAAoBA,GACxClI,EAAO0O,aAAavG,mBAAqBA,GACzCnI,EAAO0O,aAAatG,oBAAsBA,IAC5CgH,EAAgBpP,EAAO0O,kBAEpB,IAAI1O,EAAOkP,mBAChB,IAAK/F,EAAI,EAAGgG,EAAMnP,EAAOkP,mBAAmBtO,OAAYuO,EAAJhG,EAASA,IAC3D,GAAInJ,EAAOkP,mBAAmB/F,GAAGlB,iBAAmBA,GAChDjI,EAAOkP,mBAAmB/F,GAAGjB,kBAAoBA,GACjDlI,EAAOkP,mBAAmB/F,GAAGhB,mBAAqBA,GAClDnI,EAAOkP,mBAAmB/F,GAAGf,oBAAsBA,EAAmB,CACxEgH,EAAgBpP,EAAOkP,mBAAmB/F,EAC1C,OAKN,GAAK2F,GAAqC3O,SAAlBiP,GAOtB,GAHApH,EAAejI,EAAMkI,EAAgBC,EAAiBtD,EAAiBuD,EAAkBC,EAAmBC,GAC5GrI,EAAOiP,oBAAsBrK,EAEPzE,SAAlBiP,EAA6B,CAC/B,GAAIC,EACAhH,IAE0BlI,SAAxBH,EAAO0O,eACT1O,EAAO0O,iBAETW,EAAgBrP,EAAO0O,eAGWvO,SAA9BH,EAAOkP,qBACTlP,EAAOkP,uBAETG,KACArP,EAAOkP,mBAAmBI,KAAKD,IAGjCA,EAAcpH,eAAiBA,EAC/BoH,EAAcnH,gBAAkBA,EAChCmH,EAAclH,iBAAmBA,EACjCkH,EAAcjH,kBAAoBA,EAClCiH,EAAcT,cAAgB5O,EAAO8I,cACrCuG,EAAcV,eAAiB3O,EAAO+I,oBA5BxC/I,GAAOuP,aAAeH,EAAcR,cACpC5O,EAAOwP,cAAgBJ,EAAcT,cAsCvC,OAPItG,KACFtI,EAAKC,OAAOE,MAAQH,EAAKC,OAAO8I,cAChC/I,EAAKC,OAAOI,OAASL,EAAKC,OAAO+I,eACjC/I,EAAOyP,cAAe,GAGxBzP,EAAO+O,gBAAkBC,EACjBF,GAAqC3O,SAAlBiP,EAG7B,QAASM,GAAW3P,EAAMkI,EAAgBC,EAAiBtD,GAIzDoK,IAIIjO,EAAYkH,IAAmBrC,EAAkB7F,EAAMsB,MACzD4G,EAAiBlI,EAAKU,MAAMP,MAAQ4D,EAAc/D,EAAMsB,KAEtDN,EAAYmH,IAAoBtC,EAAkB7F,EAAMyB,MAC1D0G,EAAkBnI,EAAKU,MAAML,OAAS0D,EAAc/D,EAAMyB,IAG5D,IAAI2G,GAAmBpH,EAAYkH,GAAkBK,GAA6BO,GAC9ET,EAAoBrH,EAAYmH,GAAmBI,GAA6BO,EAEhF+B,GAAmB7K,EAAMkI,EAAgBC,EAAiBtD,EAAiBuD,EAAkBC,GAAmB,EAAM,YACxHV,EAAY3H,EAAMA,EAAKC,OAAOyE,WAxjDlC,GAIIiG,GAJA7I,GAAwB,EAExBmN,EAA0B,EAI1Bb,EAAW,OACXE,EAAU,MACVD,EAAY,QACZE,EAAa,SAEbzJ,GAAwB,UACxBC,GAAoB,MACpBJ,GAAoB,MAEpBrD,GAAyB,MACzBC,GAAiC,cACjCE,GAA4B,SAC5BC,GAAoC,iBAEpCiL,GAAyB,aACzBC,GAAqB,SACrBC,GAAuB,WACvBC,GAA4B,gBAC5BC,GAA2B,eAE3BO,GAAuB,aACvBE,GAAmB,SACnBI,GAAqB,WACrBlB,GAAoB,UAEpBtH,GAAwB,WACxBmC,GAAwB,WAExBhC,GAAuB,UACvBqF,GAAsB,SAEtBrC,GAA6B,YAC7BO,GAA2B,UAC3BI,GAA2B,UAE3BxB,IACFpB,IAAO,OACPE,cAAe,QACfC,OAAU,MACVE,iBAAkB,UAEhBa,IACFlB,IAAO,QACPE,cAAe,OACfC,OAAU,SACVE,iBAAkB,OAEhBV,IACFK,IAAO,OACPE,cAAe,QACfC,OAAU,MACVE,iBAAkB,UAEhBb,IACFQ,IAAO,QACPE,cAAe,QACfC,OAAU,SACVE,iBAAkB,UAEhBf,IACFU,IAAO,gBACPE,cAAe,gBACfC,OAAU,iBACVE,iBAAkB,iBAu/CpB,QACEsB,eAAgBA,EAChBpI,cAAe8P,EACf5P,UAAWA,EACXyO,wBAAyBA,KAY3B,OALqB,gBAAZ7O,WACTC,OAAOD,QAAUE,GAIV,SAASG,GAGdH,EAAcE,UAAUC,GACxBH,EAAcA,cAAcG","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n \n var POSITIVE_FLEX_IS_AUTO = false;\n \n var gCurrentGenerationCount = 0;\n \n var CSS_UNDEFINED;\n \n var CSS_LEFT = 'left';\n var CSS_TOP = 'top';\n var CSS_RIGHT = 'right';\n var CSS_BOTTOM = 'bottom';\n \n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n \n var CSS_OVERFLOW_VISIBLE = 'visible';\n var CSS_OVERFLOW_HIDDEN = 'hidden';\n \n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n var measuredDim = {\n 'row': 'measuredWidth',\n 'row-reverse': 'measuredWidth',\n 'column': 'measuredHeight',\n 'column-reverse': 'measuredHeight'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || Number.isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n \n function getFlex(node) {\n if (node.style.flex === undefined) {\n return 0;\n }\n return node.style.flex;\n }\n \n function isFlexBasisAuto(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // All flex values are auto.\n return true;\n } else {\n // A flex value > 0 implies a basis of zero.\n return getFlex(node) <= 0;\n }\n }\n \n function getFlexGrowFactor(node) {\n // Flex grow is implied by positive values for flex.\n if (getFlex(node) > 0) {\n return getFlex(node);\n }\n return 0;\n }\n \n function getFlexShrinkFactor(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // A flex shrink factor of 1 is implied by non-zero values for flex.\n if (getFlex(node) !== 0) {\n return 1;\n }\n } else {\n // A flex shrink factor of 1 is implied by negative values for flex.\n if (getFlex(node) < 0) {\n return 1;\n }\n }\n return 0;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return CSS_POSITION_RELATIVE;\n }\n \n function getOverflow(node) {\n if (node.style.overflow) {\n return node.style.overflow;\n }\n return CSS_OVERFLOW_VISIBLE;\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex !== undefined && node.style.flex !== 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[measuredDim[axis]] + getMarginAxis(node, axis);\n }\n \n function isStyleDimDefined(node, axis) { \n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n \n function isLayoutDimDefined(node, axis) { \n return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n \n function boundAxisWithinMinAndMax(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n \n function fminf(a, b) {\n if (a < b) {\n return a;\n }\n return b;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n \n // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the\n // padding and border amount.\n function boundAxis(node, axis, value) {\n return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis));\n }\n\n function setTrailingPosition(node, child, axis) {\n var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ?\n 0 :\n child.layout[measuredDim[axis]];\n child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n \n function setPosition(node, direction) {\n var mainAxis = resolveAxis(getFlexDirection(node), direction);\n var crossAxis = getCrossFlexDirection(mainAxis, direction);\n \n node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n }\n \n function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n }\n \n //\n // This is the main routine that implements a subset of the flexbox layout algorithm\n // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.\n //\n // Limitations of this algorithm, compared to the full standard:\n // * Display property is always assumed to be 'flex' except for Text nodes, which\n // are assumed to be 'inline-flex'.\n // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are\n // stacked in document order.\n // * The 'order' property is not supported. The order of flex items is always defined\n // by document order.\n // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'\n // and 'hidden' are not supported.\n // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The\n // rarely-used 'wrap-reverse' is not supported.\n // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and\n // flexBasis, this algorithm supports only the three most common combinations:\n // flex: 0 is equiavlent to flex: 0 0 auto\n // flex: n (where n is a positive value) is equivalent to flex: n 1 auto\n // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0\n // This is faster because the content doesn't need to be measured, but it's\n // less flexible because the basis is always 0 and can't be overriden with\n // the width/height attributes.\n // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto\n // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel\n // values, and the default value is 0.\n // * The 'baseline' value is not supported for alignItems and alignSelf properties.\n // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be\n // specified as pixel values, not as percentages.\n // * There is no support for calculation of dimensions based on intrinsic aspect ratios\n // (e.g. images).\n // * There is no support for forced breaks.\n // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).\n //\n // Deviations from standard:\n // * Section 4.5 of the spec indicates that all flex items have a default minimum\n // main size. For text blocks, for example, this is the width of the widest word. \n // Calculating the minimum width is expensive, so we forego it and assume a default \n // minimum main size of 0.\n // * Min/Max sizes in the main axis are not honored when resolving flexible lengths.\n // * The spec indicates that the default value for 'flexDirection' is 'row', but\n // the algorithm below assumes a default of 'column'.\n //\n // Input parameters:\n // - node: current node to be sized and layed out\n // - availableWidth & availableHeight: available size to be used for sizing the node\n // or CSS_UNDEFINED if the size is not available; interpretation depends on layout\n // flags\n // - parentDirection: the inline (text) direction within the parent (left-to-right or\n // right-to-left)\n // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)\n // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)\n // - performLayout: specifies whether the caller is interested in just the dimensions\n // of the node or it requires the entire node and its subtree to be layed out\n // (with final positions)\n //\n // Details:\n // This routine is called recursively to lay out subtrees of flexbox elements. It uses the\n // information in node.style, which is treated as a read-only input. It is responsible for\n // setting the layout.direction and layout.measured_dimensions fields for the input node as well\n // as the layout.position and layout.line_index fields for its child nodes. The\n // layout.measured_dimensions field includes any border or padding for the node but does\n // not include margins.\n //\n // The spec describes four different layout modes: \"fill available\", \"max content\", \"min content\",\n // and \"fit content\". Of these, we don't use \"min content\" because we don't support default\n // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode\n // from the spec (https://www.w3.org/TR/css3-sizing/#terms):\n // - CSS_MEASURE_MODE_UNDEFINED: max content\n // - CSS_MEASURE_MODE_EXACTLY: fill available\n // - CSS_MEASURE_MODE_AT_MOST: fit content\n // \n // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of\n // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.\n //\n function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) {\n assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n \n var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n // Set the resolved resolution in the node's layout.\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n node.layout.direction = direction;\n\n // For content (text) nodes, determine the dimensions based on the text contents.\n if (isMeasureDefined(node)) {\n var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n \n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n\n // Don't bother sizing the text if both dimensions are already defined.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n } else if (innerWidth <= 0) {\n\n // Don't bother sizing the text if there's no horizontal space.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n } else {\n\n // Measure the text under the current constraints.\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n innerWidth,\n widthMeasureMode,\n innerHeight,\n heightMeasureMode\n );\n\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.width + paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.height + paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n }\n \n return;\n }\n\n // For nodes with no children, use the available values if they were provided, or\n // the minimum size as indicated by the padding and border sizes.\n var/*int*/ childCount = node.children.length;\n if (childCount === 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n return;\n }\n\n // If we're not being asked to perform a full layout, we can handle a number of common\n // cases here without incurring the cost of the remaining function.\n if (!performLayout) {\n // If we're being asked to size the content with an at most constraint but there is no available width,\n // the measurement will always be zero.\n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 &&\n heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn));\n return;\n }\n\n if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow));\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n // If we're being asked to use an exact width/height, there's no need to measure the children.\n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n return;\n }\n }\n\n // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*bool*/ isMainAxisRow = isRowDirection(mainAxis);\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_node_t**/ firstAbsoluteChild = undefined;\n var/*css_node_t**/ currentAbsoluteChild = undefined;\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n \n var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;\n var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;\n\n // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS\n var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;\n var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;\n\n // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM\n var/*css_node_t**/ child;\n var/*int*/ i;\n var/*float*/ childWidth;\n var/*float*/ childHeight;\n var/*css_measure_mode_t*/ childWidthMeasureMode;\n var/*css_measure_mode_t*/ childHeightMeasureMode;\n for (i = 0; i < childCount; i++) {\n child = node.children[i];\n\n if (performLayout) {\n // Set the initial position (relative to the parent).\n var/*css_direction_t*/ childDirection = resolveDirection(child, direction);\n setPosition(child, childDirection);\n }\n \n // Absolute-positioned children don't participate in flex layout. Add them\n // to a list that we can process later.\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === undefined) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== undefined) {\n currentAbsoluteChild.nextChild = child;\n }\n currentAbsoluteChild = child;\n child.nextChild = undefined;\n } else {\n \n if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n \n // The width is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW));\n } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n \n // The height is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN));\n } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) {\n \n // If the basis isn't 'auto', it is assumed to be zero.\n child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));\n } else {\n \n // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n \n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n // Measure the child\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure');\n \n child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis));\n }\n }\n }\n\n // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES\n \n // Indexes of children that represent the first and last items in the line.\n var/*int*/ startOfLineIndex = 0;\n var/*int*/ endOfLineIndex = 0;\n \n // Number of lines.\n var/*int*/ lineCount = 0;\n \n // Accumulated cross dimensions of all lines so far.\n var/*float*/ totalLineCrossDim = 0;\n\n // Max main dimension of all the lines.\n var/*float*/ maxLineMainDim = 0;\n\n while (endOfLineIndex < childCount) {\n \n // Number of items on the currently line. May be different than the difference\n // between start and end indicates because we skip over absolute-positioned items.\n var/*int*/ itemsOnLine = 0;\n\n // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin\n // of all the children on the current line. This will be used in order to\n // either set the dimensions of the node if none already exist or to compute\n // the remaining space left for the flexible children.\n var/*float*/ sizeConsumedOnCurrentLine = 0;\n\n var/*float*/ totalFlexGrowFactors = 0;\n var/*float*/ totalFlexShrinkScaledFactors = 0;\n\n i = startOfLineIndex;\n\n // Maintain a linked list of the child nodes that can shrink and/or grow.\n var/*css_node_t**/ firstRelativeChild = undefined;\n var/*css_node_t**/ currentRelativeChild = undefined;\n\n // Add items to the current line until it's full or we run out of items.\n while (i < childCount) {\n child = node.children[i];\n child.lineIndex = lineCount;\n\n if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) {\n var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis);\n \n // If this is a multi-line flow and this item pushes us over the available size, we've\n // hit the end of the current line. Break out of the loop and lay out the current line.\n if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {\n break;\n }\n\n sizeConsumedOnCurrentLine += outerFlexBasis;\n itemsOnLine++;\n\n if (isFlex(child)) {\n totalFlexGrowFactors += getFlexGrowFactor(child);\n \n // Unlike the grow factor, the shrink factor is scaled relative to the child\n // dimension.\n totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;\n }\n\n // Store a private linked list of children that need to be layed out.\n if (firstRelativeChild === undefined) {\n firstRelativeChild = child;\n }\n if (currentRelativeChild !== undefined) {\n currentRelativeChild.nextChild = child;\n }\n currentRelativeChild = child;\n child.nextChild = undefined;\n }\n \n i++;\n endOfLineIndex++;\n }\n \n // If we don't need to measure the cross axis, we can skip the entire flex step.\n var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY;\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS\n // Calculate the remaining available space that needs to be allocated.\n // If the main dimension size isn't known, it is computed based on\n // the line length, so there's no more space left to distribute.\n var/*float*/ remainingFreeSpace = 0;\n if (!isUndefined(availableInnerMainDim)) {\n remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;\n } else if (sizeConsumedOnCurrentLine < 0) {\n // availableInnerMainDim is indefinite which means the node is being sized based on its content.\n // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for\n // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.\n remainingFreeSpace = -sizeConsumedOnCurrentLine;\n }\n \n var/*float*/ originalRemainingFreeSpace = remainingFreeSpace;\n var/*float*/ deltaFreeSpace = 0;\n\n if (!canSkipFlex) {\n var/*float*/ childFlexBasis;\n var/*float*/ flexShrinkScaledFactor;\n var/*float*/ flexGrowFactor;\n var/*float*/ baseMainSize;\n var/*float*/ boundMainSize;\n \n // Do two passes over the flex items to figure out how to distribute the remaining space.\n // The first pass finds the items whose min/max constraints trigger, freezes them at those\n // sizes, and excludes those sizes from the remaining space. The second pass sets the size\n // of each flexible item. It distributes the remaining space amongst the items whose min/max\n // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing\n // their min/max constraints to trigger again. \n //\n // This two pass approach for resolving min/max constraints deviates from the spec. The\n // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process\n // that needs to be repeated a variable number of times. The algorithm implemented here\n // won't handle all cases but it was simpler to implement and it mitigates performance\n // concerns because we know exactly how many passes it'll do.\n \n // First pass: detect the flex items whose min/max constraints trigger\n var/*float*/ deltaFlexShrinkScaledFactors = 0;\n var/*float*/ deltaFlexGrowFactors = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;\n }\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexGrowFactors -= flexGrowFactor;\n }\n }\n }\n \n currentRelativeChild = currentRelativeChild.nextChild;\n }\n \n totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;\n totalFlexGrowFactors += deltaFlexGrowFactors;\n remainingFreeSpace += deltaFreeSpace;\n \n // Second pass: resolve the sizes of the flexible items\n deltaFreeSpace = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n var/*float*/ updatedMainSize = childFlexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);\n }\n }\n \n deltaFreeSpace -= updatedMainSize - childFlexBasis;\n \n if (isMainAxisRow) {\n childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = availableInnerCrossDim;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n } else {\n childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = availableInnerCrossDim;\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n }\n \n var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) &&\n getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH;\n\n // Recursively call the layout algorithm for this child with the updated main size.\n layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex');\n\n currentRelativeChild = currentRelativeChild.nextChild;\n }\n }\n \n remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace;\n\n // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION\n\n // At this point, all the children have their dimensions set in the main axis.\n // Their dimensions are also set in the cross axis with the exception of items\n // that are aligned 'stretch'. We need to compute these stretch values and\n // set the final positions.\n\n // If we are using \"at most\" rules in the main axis, we won't distribute\n // any remaining space at this point.\n if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n remainingFreeSpace = 0;\n }\n\n // Use justifyContent to figure out how to allocate the remaining space\n // available in the main axis.\n if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingFreeSpace / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingFreeSpace;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingFreeSpace = fmaxf(remainingFreeSpace, 0);\n if (itemsOnLine > 1) {\n betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingFreeSpace / itemsOnLine;\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim;\n var/*float*/ crossDim = 0;\n\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n if (performLayout) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n }\n } else {\n if (performLayout) {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n }\n \n // Now that we placed the element, we need to update the variables.\n // We need to do that only for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n if (canSkipFlex) {\n // If we skipped the flex step, then we can't rely on the measuredDims because\n // they weren't computed. This means we can't call getDimWithMargin.\n mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis;\n crossDim = availableInnerCrossDim;\n } else {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n \n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n }\n }\n }\n }\n\n mainDim += trailingPaddingAndBorderMain;\n \n var/*float*/ containerCrossAxis = availableInnerCrossDim;\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n // Compute the cross axis from the max cross dimension of the children.\n containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n \n if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim);\n }\n }\n\n // If there's no flex wrap, the cross dimension is defined by the container.\n if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) {\n crossDim = availableInnerCrossDim;\n }\n\n // Clamp to the min/max size specified on the container.\n crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n\n // STEP 7: CROSS-AXIS ALIGNMENT\n // We can skip child alignment if we're just measuring the container.\n if (performLayout) {\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // If the child is absolutely positioned and has a top/left/bottom/right\n // set, override all the previously computed positions to set it correctly.\n if (isPosDefined(child, leading[crossAxis])) {\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n } else {\n child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross +\n getLeadingMargin(child, crossAxis);\n }\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n \n // If the child uses align stretch, we need to lay it out one more time, this time\n // forcing the cross-axis size to be the computed cross size for the current line.\n if (alignItem === CSS_ALIGN_STRETCH) {\n childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n var/*bool*/ isCrossSizeDefinite = false;\n \n if (isMainAxisRow) {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeight = crossDim;\n } else {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW);\n childWidth = crossDim;\n }\n \n // If the child defines a definite size for its cross axis, there's no need to stretch.\n if (!isCrossSizeDefinite) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch');\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;\n }\n }\n }\n\n totalLineCrossDim += crossDim;\n maxLineMainDim = fmaxf(maxLineMainDim, mainDim);\n\n // Reset variables for new line.\n lineCount++;\n startOfLineIndex = endOfLineIndex;\n endOfLineIndex = startOfLineIndex;\n }\n\n // STEP 8: MULTI-LINE CONTENT ALIGNMENT\n if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) {\n var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (availableInnerCrossDim > totalLineCrossDim) {\n crossDimLead = (remainingAlignContentDim / lineCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < lineCount; ++i) {\n var/*int*/ startIndex = endIndex;\n var/*int*/ j;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (j = startIndex; j < childCount; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(lineHeight,\n child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis));\n }\n }\n endIndex = j;\n lineHeight += crossDimLead;\n\n if (performLayout) {\n for (j = startIndex; j < endIndex; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n childHeight = child.layout[measuredDim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with indefinite\n // (auto) crossAxis dimension.\n }\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n // STEP 9: COMPUTING FINAL DIMENSIONS\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n\n // If the user didn't specify a width or height for the node, set the\n // dimensions based on the children.\n if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);\n } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[mainAxis]] = fmaxf(\n fminf(availableInnerMainDim + paddingAndBorderAxisMain,\n boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),\n paddingAndBorderAxisMain);\n }\n\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);\n } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[crossAxis]] = fmaxf(\n fminf(availableInnerCrossDim + paddingAndBorderAxisCross,\n boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),\n paddingAndBorderAxisCross);\n }\n \n // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN\n if (performLayout) {\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n\n // Set trailing position if necessary.\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n }\n \n // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== undefined) {\n // Now that we know the bounds of the container, perform layout again on the\n // absolutely-positioned children.\n if (performLayout) {\n\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n\n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n } else {\n // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) {\n childWidth = node.layout.measuredWidth -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) -\n (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]);\n childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);\n }\n }\n \n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n } else {\n // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) {\n childHeight = node.layout.measuredHeight -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) -\n (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]);\n childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);\n }\n }\n\n // If we're still missing one or the other dimension, measure the content.\n if (isUndefined(childWidth) || isUndefined(childHeight)) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure');\n childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout');\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]);\n }\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]);\n }\n }\n\n currentAbsoluteChild = currentAbsoluteChild.nextChild;\n }\n }\n \n function canUseCachedMeasurement(availableWidth, availableHeight,\n marginRow, marginColumn,\n widthMeasureMode, heightMeasureMode,\n cachedLayout) {\n\n // Is it an exact match?\n if (cachedLayout.availableWidth === availableWidth &&\n cachedLayout.availableHeight === availableHeight &&\n cachedLayout.widthMeasureMode === widthMeasureMode &&\n cachedLayout.heightMeasureMode === heightMeasureMode) {\n return true;\n }\n \n // If the width is an exact match, try a fuzzy match on the height.\n if (cachedLayout.availableWidth === availableWidth &&\n cachedLayout.widthMeasureMode === widthMeasureMode &&\n heightMeasureMode === CSS_MEASURE_MODE_EXACTLY &&\n availableHeight - marginColumn === cachedLayout.computedHeight) {\n return true;\n }\n \n // If the height is an exact match, try a fuzzy match on the width.\n if (cachedLayout.availableHeight === availableHeight &&\n cachedLayout.heightMeasureMode === heightMeasureMode &&\n widthMeasureMode === CSS_MEASURE_MODE_EXACTLY &&\n availableWidth - marginRow === cachedLayout.computedWidth) {\n return true;\n }\n\n return false;\n }\n \n //\n // This is a wrapper around the layoutNodeImpl function. It determines\n // whether the layout request is redundant and can be skipped.\n //\n // Parameters:\n // Input parameters are the same as layoutNodeImpl (see above)\n // Return parameter is true if layout was performed, false if skipped\n //\n function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection,\n widthMeasureMode, heightMeasureMode, performLayout, reason) {\n var layout = node.layout;\n\n var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) ||\n layout.lastParentDirection !== parentDirection;\n\n if (needToVisitNode) {\n // Invalidate the cached results.\n if (layout.cachedMeasurements !== undefined) {\n layout.cachedMeasurements = []; \n }\n if (layout.cachedLayout !== undefined) {\n layout.cachedLayout.widthMeasureMode = undefined;\n layout.cachedLayout.heightMeasureMode = undefined;\n }\n }\n \n var i;\n var len;\n var cachedResults;\n \n // Determine whether the results are already cached. We maintain a separate\n // cache for layouts and measurements. A layout operation modifies the positions\n // and dimensions for nodes in the subtree. The algorithm assumes that each node\n // gets layed out a maximum of one time per tree layout, but multiple measurements\n // may be required to resolve all of the flex dimensions.\n // We handle nodes with measure functions specially here because they are the most\n // expensive to measure, so it's worth avoiding redundant measurements if at all possible.\n if (isMeasureDefined(node)) {\n var marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n \n // First, try to use the layout cache.\n if (layout.cachedLayout &&\n canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,\n widthMeasureMode, heightMeasureMode, layout.cachedLayout)) {\n cachedResults = layout.cachedLayout;\n } else if (layout.cachedMeasurements) {\n // Try to use the measurement cache.\n for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,\n widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i])) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n } else if (performLayout) {\n if (layout.cachedLayout &&\n layout.cachedLayout.availableWidth === availableWidth &&\n layout.cachedLayout.availableHeight === availableHeight &&\n layout.cachedLayout.widthMeasureMode === widthMeasureMode &&\n layout.cachedLayout.heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedLayout;\n }\n } else if (layout.cachedMeasurements) {\n for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (layout.cachedMeasurements[i].availableWidth === availableWidth &&\n layout.cachedMeasurements[i].availableHeight === availableHeight &&\n layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode &&\n layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n \n if (!needToVisitNode && cachedResults !== undefined) {\n layout.measureWidth = cachedResults.computedWidth;\n layout.measureHeight = cachedResults.computedHeight;\n } else {\n layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);\n layout.lastParentDirection = parentDirection;\n \n if (cachedResults === undefined) {\n var newCacheEntry;\n if (performLayout) {\n // Use the single layout cache entry.\n if (layout.cachedLayout === undefined) {\n layout.cachedLayout = {};\n }\n newCacheEntry = layout.cachedLayout;\n } else {\n // Allocate a new measurement cache entry.\n if (layout.cachedMeasurements === undefined) {\n layout.cachedMeasurements = [];\n }\n newCacheEntry = {};\n layout.cachedMeasurements.push(newCacheEntry);\n }\n \n newCacheEntry.availableWidth = availableWidth;\n newCacheEntry.availableHeight = availableHeight;\n newCacheEntry.widthMeasureMode = widthMeasureMode;\n newCacheEntry.heightMeasureMode = heightMeasureMode;\n newCacheEntry.computedWidth = layout.measuredWidth;\n newCacheEntry.computedHeight = layout.measuredHeight;\n }\n }\n \n if (performLayout) {\n node.layout.width = node.layout.measuredWidth;\n node.layout.height = node.layout.measuredHeight;\n layout.shouldUpdate = true;\n }\n \n layout.generationCount = gCurrentGenerationCount;\n return (needToVisitNode || cachedResults === undefined);\n }\n \n function layoutNode(node, availableWidth, availableHeight, parentDirection) {\n // Increment the generation count. This will force the recursive routine to visit\n // all dirty nodes at least once. Subsequent visits will be skipped if the input\n // parameters don't change.\n gCurrentGenerationCount++;\n \n // If the caller didn't specify a height/width, use the dimensions\n // specified in the style.\n if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n }\n if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) {\n setPosition(node, node.layout.direction);\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes,\n canUseCachedMeasurement: canUseCachedMeasurement\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file +{"version":3,"sources":["css-layout.js"],"names":["root","factory","define","amd","exports","module","computeLayout","this","fillNodes","node","layout","isDirty","width","undefined","height","top","left","right","bottom","style","children","measure","length","Error","forEach","isUndefined","value","Number","isNaN","isRowDirection","flexDirection","CSS_FLEX_DIRECTION_ROW","CSS_FLEX_DIRECTION_ROW_REVERSE","isColumnDirection","CSS_FLEX_DIRECTION_COLUMN","CSS_FLEX_DIRECTION_COLUMN_REVERSE","getFlex","flex","isFlexBasisAuto","POSITIVE_FLEX_IS_AUTO","getFlexGrowFactor","getFlexShrinkFactor","getLeadingMargin","axis","marginStart","marginLeft","marginRight","marginTop","marginBottom","margin","getTrailingMargin","marginEnd","getLeadingPadding","paddingStart","paddingLeft","paddingRight","paddingTop","paddingBottom","padding","getTrailingPadding","paddingEnd","getLeadingBorder","borderStartWidth","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth","borderWidth","getTrailingBorder","borderEndWidth","getLeadingPaddingAndBorder","getTrailingPaddingAndBorder","getMarginAxis","getPaddingAndBorderAxis","getJustifyContent","justifyContent","getAlignContent","alignContent","getAlignItem","child","alignSelf","alignItems","resolveAxis","direction","CSS_DIRECTION_RTL","resolveDirection","parentDirection","CSS_DIRECTION_INHERIT","CSS_DIRECTION_LTR","getFlexDirection","getCrossFlexDirection","getPositionType","position","CSS_POSITION_RELATIVE","getOverflow","overflow","CSS_OVERFLOW_VISIBLE","isFlex","isFlexWrap","flexWrap","getDimWithMargin","measuredDim","isStyleDimDefined","dim","isLayoutDimDefined","isPosDefined","pos","isMeasureDefined","getPosition","boundAxisWithinMinAndMax","min","row","minWidth","row-reverse","column","minHeight","column-reverse","max","maxWidth","maxHeight","boundValue","fminf","a","b","fmaxf","boundAxis","setTrailingPosition","size","CSS_POSITION_ABSOLUTE","trailing","getRelativePosition","leading","setPosition","mainAxis","crossAxis","assert","condition","message","layoutNodeImpl","availableWidth","availableHeight","widthMeasureMode","heightMeasureMode","performLayout","CSS_MEASURE_MODE_UNDEFINED","paddingAndBorderAxisRow","paddingAndBorderAxisColumn","marginAxisRow","marginAxisColumn","innerWidth","innerHeight","CSS_MEASURE_MODE_EXACTLY","measuredWidth","measuredHeight","measureDim","CSS_MEASURE_MODE_AT_MOST","childCount","i","childWidth","childHeight","childWidthMeasureMode","childHeightMeasureMode","isMainAxisRow","isNodeFlexWrap","firstAbsoluteChild","currentAbsoluteChild","leadingPaddingAndBorderMain","trailingPaddingAndBorderMain","leadingPaddingAndBorderCross","paddingAndBorderAxisMain","paddingAndBorderAxisCross","measureModeMainDim","measureModeCrossDim","availableInnerWidth","availableInnerHeight","availableInnerMainDim","availableInnerCrossDim","childDirection","nextChild","flexBasis","CSS_UNDEFINED","CSS_OVERFLOW_HIDDEN","layoutNodeInternal","startOfLineIndex","endOfLineIndex","lineCount","totalLineCrossDim","maxLineMainDim","itemsOnLine","sizeConsumedOnCurrentLine","totalFlexGrowFactors","totalFlexShrinkScaledFactors","firstRelativeChild","currentRelativeChild","lineIndex","outerFlexBasis","canSkipFlex","leadingMainDim","betweenMainDim","remainingFreeSpace","originalRemainingFreeSpace","deltaFreeSpace","childFlexBasis","flexShrinkScaledFactor","flexGrowFactor","baseMainSize","boundMainSize","deltaFlexShrinkScaledFactors","deltaFlexGrowFactors","updatedMainSize","requiresStretchLayout","CSS_ALIGN_STRETCH","CSS_JUSTIFY_FLEX_START","CSS_JUSTIFY_CENTER","CSS_JUSTIFY_FLEX_END","CSS_JUSTIFY_SPACE_BETWEEN","CSS_JUSTIFY_SPACE_AROUND","mainDim","crossDim","containerCrossAxis","leadingCrossDim","alignItem","isCrossSizeDefinite","CSS_ALIGN_FLEX_START","remainingCrossDim","CSS_ALIGN_CENTER","remainingAlignContentDim","crossDimLead","currentLead","CSS_ALIGN_FLEX_END","endIndex","j","startIndex","lineHeight","alignContentAlignItem","needsMainTrailingPos","needsCrossTrailingPos","CSS_LEFT","CSS_RIGHT","CSS_TOP","CSS_BOTTOM","canUseCachedMeasurement","marginRow","marginColumn","cachedLayout","computedHeight","computedWidth","reason","needToVisitNode","generationCount","gCurrentGenerationCount","lastParentDirection","cachedMeasurements","len","cachedResults","newCacheEntry","push","measureWidth","measureHeight","shouldUpdate","layoutNode"],"mappings":"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","file":"css-layout.min.js","sourcesContent":["// UMD (Universal Module Definition)\n// See https://github.com/umdjs/umd for reference\n//\n// This file uses the following specific UMD implementation:\n// https://github.com/umdjs/umd/blob/master/templates/returnExports.js\n(function(root, factory) {\n if (typeof define === 'function' && define.amd) {\n // AMD. Register as an anonymous module.\n define([], factory);\n } else if (typeof exports === 'object') {\n // Node. Does not work with strict CommonJS, but\n // only CommonJS-like environments that support module.exports,\n // like Node.\n module.exports = factory();\n } else {\n // Browser globals (root is window)\n root.computeLayout = factory();\n }\n}(this, function() {\n /**\n * Copyright (c) 2014, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the BSD-style license found in the\n * LICENSE file in the root directory of this source tree. An additional grant\n * of patent rights can be found in the PATENTS file in the same directory.\n */\n\nvar computeLayout = (function() {\n \n var POSITIVE_FLEX_IS_AUTO = false;\n \n var gCurrentGenerationCount = 0;\n \n var CSS_UNDEFINED;\n \n var CSS_LEFT = 'left';\n var CSS_TOP = 'top';\n var CSS_RIGHT = 'right';\n var CSS_BOTTOM = 'bottom';\n \n var CSS_DIRECTION_INHERIT = 'inherit';\n var CSS_DIRECTION_LTR = 'ltr';\n var CSS_DIRECTION_RTL = 'rtl';\n\n var CSS_FLEX_DIRECTION_ROW = 'row';\n var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';\n var CSS_FLEX_DIRECTION_COLUMN = 'column';\n var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';\n\n var CSS_JUSTIFY_FLEX_START = 'flex-start';\n var CSS_JUSTIFY_CENTER = 'center';\n var CSS_JUSTIFY_FLEX_END = 'flex-end';\n var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';\n var CSS_JUSTIFY_SPACE_AROUND = 'space-around';\n\n var CSS_ALIGN_FLEX_START = 'flex-start';\n var CSS_ALIGN_CENTER = 'center';\n var CSS_ALIGN_FLEX_END = 'flex-end';\n var CSS_ALIGN_STRETCH = 'stretch';\n\n var CSS_POSITION_RELATIVE = 'relative';\n var CSS_POSITION_ABSOLUTE = 'absolute';\n \n var CSS_OVERFLOW_VISIBLE = 'visible';\n var CSS_OVERFLOW_HIDDEN = 'hidden';\n \n var CSS_MEASURE_MODE_UNDEFINED = 'undefined';\n var CSS_MEASURE_MODE_EXACTLY = 'exactly';\n var CSS_MEASURE_MODE_AT_MOST = 'at-most';\n\n var leading = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var trailing = {\n 'row': 'right',\n 'row-reverse': 'left',\n 'column': 'bottom',\n 'column-reverse': 'top'\n };\n var pos = {\n 'row': 'left',\n 'row-reverse': 'right',\n 'column': 'top',\n 'column-reverse': 'bottom'\n };\n var dim = {\n 'row': 'width',\n 'row-reverse': 'width',\n 'column': 'height',\n 'column-reverse': 'height'\n };\n var measuredDim = {\n 'row': 'measuredWidth',\n 'row-reverse': 'measuredWidth',\n 'column': 'measuredHeight',\n 'column-reverse': 'measuredHeight'\n };\n\n // When transpiled to Java / C the node type has layout, children and style\n // properties. For the JavaScript version this function adds these properties\n // if they don't already exist.\n function fillNodes(node) {\n if (!node.layout || node.isDirty) {\n node.layout = {\n width: undefined,\n height: undefined,\n top: 0,\n left: 0,\n right: 0,\n bottom: 0\n };\n }\n\n if (!node.style) {\n node.style = {};\n }\n\n if (!node.children) {\n node.children = [];\n }\n\n if (node.style.measure && node.children && node.children.length) {\n throw new Error('Using custom measure function is supported only for leaf nodes.');\n }\n\n node.children.forEach(fillNodes);\n return node;\n }\n\n function isUndefined(value) {\n return value === undefined || Number.isNaN(value);\n }\n\n function isRowDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_ROW ||\n flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;\n }\n\n function isColumnDirection(flexDirection) {\n return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||\n flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;\n }\n \n function getFlex(node) {\n if (node.style.flex === undefined) {\n return 0;\n }\n return node.style.flex;\n }\n \n function isFlexBasisAuto(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // All flex values are auto.\n return true;\n } else {\n // A flex value > 0 implies a basis of zero.\n return getFlex(node) <= 0;\n }\n }\n \n function getFlexGrowFactor(node) {\n // Flex grow is implied by positive values for flex.\n if (getFlex(node) > 0) {\n return getFlex(node);\n }\n return 0;\n }\n \n function getFlexShrinkFactor(node) {\n if (POSITIVE_FLEX_IS_AUTO) {\n // A flex shrink factor of 1 is implied by non-zero values for flex.\n if (getFlex(node) !== 0) {\n return 1;\n }\n } else {\n // A flex shrink factor of 1 is implied by negative values for flex.\n if (getFlex(node) < 0) {\n return 1;\n }\n }\n return 0;\n }\n\n function getLeadingMargin(node, axis) {\n if (node.style.marginStart !== undefined && isRowDirection(axis)) {\n return node.style.marginStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginLeft; break;\n case 'row-reverse': value = node.style.marginRight; break;\n case 'column': value = node.style.marginTop; break;\n case 'column-reverse': value = node.style.marginBottom; break;\n }\n\n if (value !== undefined) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getTrailingMargin(node, axis) {\n if (node.style.marginEnd !== undefined && isRowDirection(axis)) {\n return node.style.marginEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.marginRight; break;\n case 'row-reverse': value = node.style.marginLeft; break;\n case 'column': value = node.style.marginBottom; break;\n case 'column-reverse': value = node.style.marginTop; break;\n }\n\n if (value != null) {\n return value;\n }\n\n if (node.style.margin !== undefined) {\n return node.style.margin;\n }\n\n return 0;\n }\n\n function getLeadingPadding(node, axis) {\n if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0\n && isRowDirection(axis)) {\n return node.style.paddingStart;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingLeft; break;\n case 'row-reverse': value = node.style.paddingRight; break;\n case 'column': value = node.style.paddingTop; break;\n case 'column-reverse': value = node.style.paddingBottom; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getTrailingPadding(node, axis) {\n if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0\n && isRowDirection(axis)) {\n return node.style.paddingEnd;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.paddingRight; break;\n case 'row-reverse': value = node.style.paddingLeft; break;\n case 'column': value = node.style.paddingBottom; break;\n case 'column-reverse': value = node.style.paddingTop; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.padding !== undefined && node.style.padding >= 0) {\n return node.style.padding;\n }\n\n return 0;\n }\n\n function getLeadingBorder(node, axis) {\n if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderStartWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderLeftWidth; break;\n case 'row-reverse': value = node.style.borderRightWidth; break;\n case 'column': value = node.style.borderTopWidth; break;\n case 'column-reverse': value = node.style.borderBottomWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getTrailingBorder(node, axis) {\n if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0\n && isRowDirection(axis)) {\n return node.style.borderEndWidth;\n }\n\n var value = null;\n switch (axis) {\n case 'row': value = node.style.borderRightWidth; break;\n case 'row-reverse': value = node.style.borderLeftWidth; break;\n case 'column': value = node.style.borderBottomWidth; break;\n case 'column-reverse': value = node.style.borderTopWidth; break;\n }\n\n if (value != null && value >= 0) {\n return value;\n }\n\n if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {\n return node.style.borderWidth;\n }\n\n return 0;\n }\n\n function getLeadingPaddingAndBorder(node, axis) {\n return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);\n }\n\n function getTrailingPaddingAndBorder(node, axis) {\n return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);\n }\n\n function getMarginAxis(node, axis) {\n return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);\n }\n\n function getPaddingAndBorderAxis(node, axis) {\n return getLeadingPaddingAndBorder(node, axis) +\n getTrailingPaddingAndBorder(node, axis);\n }\n\n function getJustifyContent(node) {\n if (node.style.justifyContent) {\n return node.style.justifyContent;\n }\n return 'flex-start';\n }\n\n function getAlignContent(node) {\n if (node.style.alignContent) {\n return node.style.alignContent;\n }\n return 'flex-start';\n }\n\n function getAlignItem(node, child) {\n if (child.style.alignSelf) {\n return child.style.alignSelf;\n }\n if (node.style.alignItems) {\n return node.style.alignItems;\n }\n return 'stretch';\n }\n\n function resolveAxis(axis, direction) {\n if (direction === CSS_DIRECTION_RTL) {\n if (axis === CSS_FLEX_DIRECTION_ROW) {\n return CSS_FLEX_DIRECTION_ROW_REVERSE;\n } else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {\n return CSS_FLEX_DIRECTION_ROW;\n }\n }\n\n return axis;\n }\n\n function resolveDirection(node, parentDirection) {\n var direction;\n if (node.style.direction) {\n direction = node.style.direction;\n } else {\n direction = CSS_DIRECTION_INHERIT;\n }\n\n if (direction === CSS_DIRECTION_INHERIT) {\n direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);\n }\n\n return direction;\n }\n\n function getFlexDirection(node) {\n if (node.style.flexDirection) {\n return node.style.flexDirection;\n }\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n\n function getCrossFlexDirection(flexDirection, direction) {\n if (isColumnDirection(flexDirection)) {\n return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);\n } else {\n return CSS_FLEX_DIRECTION_COLUMN;\n }\n }\n\n function getPositionType(node) {\n if (node.style.position) {\n return node.style.position;\n }\n return CSS_POSITION_RELATIVE;\n }\n \n function getOverflow(node) {\n if (node.style.overflow) {\n return node.style.overflow;\n }\n return CSS_OVERFLOW_VISIBLE;\n }\n\n function isFlex(node) {\n return (\n getPositionType(node) === CSS_POSITION_RELATIVE &&\n node.style.flex !== undefined && node.style.flex !== 0\n );\n }\n\n function isFlexWrap(node) {\n return node.style.flexWrap === 'wrap';\n }\n\n function getDimWithMargin(node, axis) {\n return node.layout[measuredDim[axis]] + getMarginAxis(node, axis);\n }\n \n function isStyleDimDefined(node, axis) { \n return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;\n }\n \n function isLayoutDimDefined(node, axis) { \n return node.layout[measuredDim[axis]] !== undefined && node.layout[measuredDim[axis]] >= 0;\n }\n\n function isPosDefined(node, pos) {\n return node.style[pos] !== undefined;\n }\n\n function isMeasureDefined(node) {\n return node.style.measure !== undefined;\n }\n\n function getPosition(node, pos) {\n if (node.style[pos] !== undefined) {\n return node.style[pos];\n }\n return 0;\n }\n \n function boundAxisWithinMinAndMax(node, axis, value) {\n var min = {\n 'row': node.style.minWidth,\n 'row-reverse': node.style.minWidth,\n 'column': node.style.minHeight,\n 'column-reverse': node.style.minHeight\n }[axis];\n\n var max = {\n 'row': node.style.maxWidth,\n 'row-reverse': node.style.maxWidth,\n 'column': node.style.maxHeight,\n 'column-reverse': node.style.maxHeight\n }[axis];\n\n var boundValue = value;\n if (max !== undefined && max >= 0 && boundValue > max) {\n boundValue = max;\n }\n if (min !== undefined && min >= 0 && boundValue < min) {\n boundValue = min;\n }\n return boundValue;\n }\n \n function fminf(a, b) {\n if (a < b) {\n return a;\n }\n return b;\n }\n\n function fmaxf(a, b) {\n if (a > b) {\n return a;\n }\n return b;\n }\n \n // Like boundAxisWithinMinAndMax but also ensures that the value doesn't go below the\n // padding and border amount.\n function boundAxis(node, axis, value) {\n return fmaxf(boundAxisWithinMinAndMax(node, axis, value), getPaddingAndBorderAxis(node, axis));\n }\n\n function setTrailingPosition(node, child, axis) {\n var size = (getPositionType(child) === CSS_POSITION_ABSOLUTE) ?\n 0 :\n child.layout[measuredDim[axis]];\n child.layout[trailing[axis]] = node.layout[measuredDim[axis]] - size - child.layout[pos[axis]];\n }\n\n // If both left and right are defined, then use left. Otherwise return\n // +left or -right depending on which is defined.\n function getRelativePosition(node, axis) {\n if (node.style[leading[axis]] !== undefined) {\n return getPosition(node, leading[axis]);\n }\n return -getPosition(node, trailing[axis]);\n }\n \n function setPosition(node, direction) {\n var mainAxis = resolveAxis(getFlexDirection(node), direction);\n var crossAxis = getCrossFlexDirection(mainAxis, direction);\n \n node.layout[leading[mainAxis]] = getLeadingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[trailing[mainAxis]] = getTrailingMargin(node, mainAxis) +\n getRelativePosition(node, mainAxis);\n node.layout[leading[crossAxis]] = getLeadingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n node.layout[trailing[crossAxis]] = getTrailingMargin(node, crossAxis) +\n getRelativePosition(node, crossAxis);\n }\n \n function assert(condition, message) {\n if (!condition) {\n throw new Error(message);\n }\n }\n \n //\n // This is the main routine that implements a subset of the flexbox layout algorithm\n // described in the W3C CSS documentation: https://www.w3.org/TR/css3-flexbox/.\n //\n // Limitations of this algorithm, compared to the full standard:\n // * Display property is always assumed to be 'flex' except for Text nodes, which\n // are assumed to be 'inline-flex'.\n // * The 'zIndex' property (or any form of z ordering) is not supported. Nodes are\n // stacked in document order.\n // * The 'order' property is not supported. The order of flex items is always defined\n // by document order.\n // * The 'visibility' property is always assumed to be 'visible'. Values of 'collapse'\n // and 'hidden' are not supported.\n // * The 'wrap' property supports only 'nowrap' (which is the default) or 'wrap'. The\n // rarely-used 'wrap-reverse' is not supported.\n // * Rather than allowing arbitrary combinations of flexGrow, flexShrink and\n // flexBasis, this algorithm supports only the three most common combinations:\n // flex: 0 is equiavlent to flex: 0 0 auto\n // flex: n (where n is a positive value) is equivalent to flex: n 1 auto\n // If POSITIVE_FLEX_IS_AUTO is 0, then it is equivalent to flex: n 0 0\n // This is faster because the content doesn't need to be measured, but it's\n // less flexible because the basis is always 0 and can't be overriden with\n // the width/height attributes.\n // flex: -1 (or any negative value) is equivalent to flex: 0 1 auto\n // * Margins cannot be specified as 'auto'. They must be specified in terms of pixel\n // values, and the default value is 0.\n // * The 'baseline' value is not supported for alignItems and alignSelf properties.\n // * Values of width, maxWidth, minWidth, height, maxHeight and minHeight must be\n // specified as pixel values, not as percentages.\n // * There is no support for calculation of dimensions based on intrinsic aspect ratios\n // (e.g. images).\n // * There is no support for forced breaks.\n // * It does not support vertical inline directions (top-to-bottom or bottom-to-top text).\n //\n // Deviations from standard:\n // * Section 4.5 of the spec indicates that all flex items have a default minimum\n // main size. For text blocks, for example, this is the width of the widest word. \n // Calculating the minimum width is expensive, so we forego it and assume a default \n // minimum main size of 0.\n // * Min/Max sizes in the main axis are not honored when resolving flexible lengths.\n // * The spec indicates that the default value for 'flexDirection' is 'row', but\n // the algorithm below assumes a default of 'column'.\n //\n // Input parameters:\n // - node: current node to be sized and layed out\n // - availableWidth & availableHeight: available size to be used for sizing the node\n // or CSS_UNDEFINED if the size is not available; interpretation depends on layout\n // flags\n // - parentDirection: the inline (text) direction within the parent (left-to-right or\n // right-to-left)\n // - widthMeasureMode: indicates the sizing rules for the width (see below for explanation)\n // - heightMeasureMode: indicates the sizing rules for the height (see below for explanation)\n // - performLayout: specifies whether the caller is interested in just the dimensions\n // of the node or it requires the entire node and its subtree to be layed out\n // (with final positions)\n //\n // Details:\n // This routine is called recursively to lay out subtrees of flexbox elements. It uses the\n // information in node.style, which is treated as a read-only input. It is responsible for\n // setting the layout.direction and layout.measured_dimensions fields for the input node as well\n // as the layout.position and layout.line_index fields for its child nodes. The\n // layout.measured_dimensions field includes any border or padding for the node but does\n // not include margins.\n //\n // The spec describes four different layout modes: \"fill available\", \"max content\", \"min content\",\n // and \"fit content\". Of these, we don't use \"min content\" because we don't support default\n // minimum main sizes (see above for details). Each of our measure modes maps to a layout mode\n // from the spec (https://www.w3.org/TR/css3-sizing/#terms):\n // - CSS_MEASURE_MODE_UNDEFINED: max content\n // - CSS_MEASURE_MODE_EXACTLY: fill available\n // - CSS_MEASURE_MODE_AT_MOST: fit content\n // \n // When calling layoutNodeImpl and layoutNodeInternal, if the caller passes an available size of\n // undefined then it must also pass a measure mode of CSS_MEASURE_MODE_UNDEFINED in that dimension.\n //\n function layoutNodeImpl(node, availableWidth, availableHeight, /*css_direction_t*/parentDirection, widthMeasureMode, heightMeasureMode, performLayout) {\n assert(isUndefined(availableWidth) ? widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableWidth is indefinite so widthMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n assert(isUndefined(availableHeight) ? heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED : true, 'availableHeight is indefinite so heightMeasureMode must be CSS_MEASURE_MODE_UNDEFINED');\n \n var/*float*/ paddingAndBorderAxisRow = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n var/*float*/ marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var/*float*/ marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n\n // Set the resolved resolution in the node's layout.\n var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);\n node.layout.direction = direction;\n\n // For content (text) nodes, determine the dimensions based on the text contents.\n if (isMeasureDefined(node)) {\n var/*float*/ innerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ innerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n \n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n\n // Don't bother sizing the text if both dimensions are already defined.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n } else if (innerWidth <= 0 || innerHeight <= 0) {\n\n // Don't bother sizing the text if there's no horizontal or vertical space.\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n } else {\n\n // Measure the text under the current constraints.\n var/*css_dim_t*/ measureDim = node.style.measure(\n /*(c)!node->context,*/\n /*(java)!layoutContext.measureOutput,*/\n innerWidth,\n widthMeasureMode,\n innerHeight,\n heightMeasureMode\n );\n\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.width + paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n measureDim.height + paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n }\n \n return;\n }\n\n // For nodes with no children, use the available values if they were provided, or\n // the minimum size as indicated by the padding and border sizes.\n var/*int*/ childCount = node.children.length;\n if (childCount === 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW,\n (widthMeasureMode === CSS_MEASURE_MODE_UNDEFINED || widthMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisRow :\n availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN,\n (heightMeasureMode === CSS_MEASURE_MODE_UNDEFINED || heightMeasureMode === CSS_MEASURE_MODE_AT_MOST) ?\n paddingAndBorderAxisColumn :\n availableHeight - marginAxisColumn);\n return;\n }\n\n // If we're not being asked to perform a full layout, we can handle a number of common\n // cases here without incurring the cost of the remaining function.\n if (!performLayout) {\n // If we're being asked to size the content with an at most constraint but there is no available width,\n // the measurement will always be zero.\n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0 &&\n heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n if (widthMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableWidth <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, isUndefined(availableHeight) ? 0 : (availableHeight - marginAxisColumn));\n return;\n }\n\n if (heightMeasureMode === CSS_MEASURE_MODE_AT_MOST && availableHeight <= 0) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, isUndefined(availableWidth) ? 0 : (availableWidth - marginAxisRow));\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0);\n return;\n }\n \n // If we're being asked to use an exact width/height, there's no need to measure the children.\n if (widthMeasureMode === CSS_MEASURE_MODE_EXACTLY && heightMeasureMode === CSS_MEASURE_MODE_EXACTLY) {\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n return;\n }\n }\n\n // STEP 1: CALCULATE VALUES FOR REMAINDER OF ALGORITHM\n var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);\n var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);\n var/*bool*/ isMainAxisRow = isRowDirection(mainAxis);\n var/*css_justify_t*/ justifyContent = getJustifyContent(node);\n var/*bool*/ isNodeFlexWrap = isFlexWrap(node);\n\n var/*css_node_t**/ firstAbsoluteChild = undefined;\n var/*css_node_t**/ currentAbsoluteChild = undefined;\n\n var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);\n var/*float*/ trailingPaddingAndBorderMain = getTrailingPaddingAndBorder(node, mainAxis);\n var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);\n var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);\n var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);\n \n var/*css_measure_mode_t*/ measureModeMainDim = isMainAxisRow ? widthMeasureMode : heightMeasureMode;\n var/*css_measure_mode_t*/ measureModeCrossDim = isMainAxisRow ? heightMeasureMode : widthMeasureMode;\n\n // STEP 2: DETERMINE AVAILABLE SIZE IN MAIN AND CROSS DIRECTIONS\n var/*float*/ availableInnerWidth = availableWidth - marginAxisRow - paddingAndBorderAxisRow;\n var/*float*/ availableInnerHeight = availableHeight - marginAxisColumn - paddingAndBorderAxisColumn;\n var/*float*/ availableInnerMainDim = isMainAxisRow ? availableInnerWidth : availableInnerHeight;\n var/*float*/ availableInnerCrossDim = isMainAxisRow ? availableInnerHeight : availableInnerWidth;\n\n // STEP 3: DETERMINE FLEX BASIS FOR EACH ITEM\n var/*css_node_t**/ child;\n var/*int*/ i;\n var/*float*/ childWidth;\n var/*float*/ childHeight;\n var/*css_measure_mode_t*/ childWidthMeasureMode;\n var/*css_measure_mode_t*/ childHeightMeasureMode;\n for (i = 0; i < childCount; i++) {\n child = node.children[i];\n\n if (performLayout) {\n // Set the initial position (relative to the parent).\n var/*css_direction_t*/ childDirection = resolveDirection(child, direction);\n setPosition(child, childDirection);\n }\n \n // Absolute-positioned children don't participate in flex layout. Add them\n // to a list that we can process later.\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n\n // Store a private linked list of absolutely positioned children\n // so that we can efficiently traverse them later.\n if (firstAbsoluteChild === undefined) {\n firstAbsoluteChild = child;\n }\n if (currentAbsoluteChild !== undefined) {\n currentAbsoluteChild.nextChild = child;\n }\n currentAbsoluteChild = child;\n child.nextChild = undefined;\n } else {\n \n if (isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n \n // The width is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.width, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_ROW));\n } else if (!isMainAxisRow && isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n \n // The height is definite, so use that as the flex basis.\n child.layout.flexBasis = fmaxf(child.style.height, getPaddingAndBorderAxis(child, CSS_FLEX_DIRECTION_COLUMN));\n } else if (!isFlexBasisAuto(child) && !isUndefined(availableInnerMainDim)) {\n \n // If the basis isn't 'auto', it is assumed to be zero.\n child.layout.flexBasis = fmaxf(0, getPaddingAndBorderAxis(child, mainAxis));\n } else {\n \n // Compute the flex basis and hypothetical main size (i.e. the clamped flex basis).\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n childWidthMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n childHeightMeasureMode = CSS_MEASURE_MODE_UNDEFINED;\n \n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = child.style.width + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n if (isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = child.style.height + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n // Measure the child\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'measure');\n \n child.layout.flexBasis = fmaxf(isMainAxisRow ? child.layout.measuredWidth : child.layout.measuredHeight, getPaddingAndBorderAxis(child, mainAxis));\n }\n }\n }\n\n // STEP 4: COLLECT FLEX ITEMS INTO FLEX LINES\n \n // Indexes of children that represent the first and last items in the line.\n var/*int*/ startOfLineIndex = 0;\n var/*int*/ endOfLineIndex = 0;\n \n // Number of lines.\n var/*int*/ lineCount = 0;\n \n // Accumulated cross dimensions of all lines so far.\n var/*float*/ totalLineCrossDim = 0;\n\n // Max main dimension of all the lines.\n var/*float*/ maxLineMainDim = 0;\n\n while (endOfLineIndex < childCount) {\n \n // Number of items on the currently line. May be different than the difference\n // between start and end indicates because we skip over absolute-positioned items.\n var/*int*/ itemsOnLine = 0;\n\n // sizeConsumedOnCurrentLine is accumulation of the dimensions and margin\n // of all the children on the current line. This will be used in order to\n // either set the dimensions of the node if none already exist or to compute\n // the remaining space left for the flexible children.\n var/*float*/ sizeConsumedOnCurrentLine = 0;\n\n var/*float*/ totalFlexGrowFactors = 0;\n var/*float*/ totalFlexShrinkScaledFactors = 0;\n\n i = startOfLineIndex;\n\n // Maintain a linked list of the child nodes that can shrink and/or grow.\n var/*css_node_t**/ firstRelativeChild = undefined;\n var/*css_node_t**/ currentRelativeChild = undefined;\n\n // Add items to the current line until it's full or we run out of items.\n while (i < childCount) {\n child = node.children[i];\n child.lineIndex = lineCount;\n\n if (getPositionType(child) !== CSS_POSITION_ABSOLUTE) {\n var/*float*/ outerFlexBasis = child.layout.flexBasis + getMarginAxis(child, mainAxis);\n \n // If this is a multi-line flow and this item pushes us over the available size, we've\n // hit the end of the current line. Break out of the loop and lay out the current line.\n if (sizeConsumedOnCurrentLine + outerFlexBasis > availableInnerMainDim && isNodeFlexWrap && itemsOnLine > 0) {\n break;\n }\n\n sizeConsumedOnCurrentLine += outerFlexBasis;\n itemsOnLine++;\n\n if (isFlex(child)) {\n totalFlexGrowFactors += getFlexGrowFactor(child);\n \n // Unlike the grow factor, the shrink factor is scaled relative to the child\n // dimension.\n totalFlexShrinkScaledFactors += getFlexShrinkFactor(child) * child.layout.flexBasis;\n }\n\n // Store a private linked list of children that need to be layed out.\n if (firstRelativeChild === undefined) {\n firstRelativeChild = child;\n }\n if (currentRelativeChild !== undefined) {\n currentRelativeChild.nextChild = child;\n }\n currentRelativeChild = child;\n child.nextChild = undefined;\n }\n \n i++;\n endOfLineIndex++;\n }\n \n // If we don't need to measure the cross axis, we can skip the entire flex step.\n var/*bool*/ canSkipFlex = !performLayout && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY;\n\n // In order to position the elements in the main axis, we have two\n // controls. The space between the beginning and the first element\n // and the space between each two elements.\n var/*float*/ leadingMainDim = 0;\n var/*float*/ betweenMainDim = 0;\n\n // STEP 5: RESOLVING FLEXIBLE LENGTHS ON MAIN AXIS\n // Calculate the remaining available space that needs to be allocated.\n // If the main dimension size isn't known, it is computed based on\n // the line length, so there's no more space left to distribute.\n var/*float*/ remainingFreeSpace = 0;\n if (!isUndefined(availableInnerMainDim)) {\n remainingFreeSpace = availableInnerMainDim - sizeConsumedOnCurrentLine;\n } else if (sizeConsumedOnCurrentLine < 0) {\n // availableInnerMainDim is indefinite which means the node is being sized based on its content.\n // sizeConsumedOnCurrentLine is negative which means the node will allocate 0 pixels for\n // its content. Consequently, remainingFreeSpace is 0 - sizeConsumedOnCurrentLine.\n remainingFreeSpace = -sizeConsumedOnCurrentLine;\n }\n \n var/*float*/ originalRemainingFreeSpace = remainingFreeSpace;\n var/*float*/ deltaFreeSpace = 0;\n\n if (!canSkipFlex) {\n var/*float*/ childFlexBasis;\n var/*float*/ flexShrinkScaledFactor;\n var/*float*/ flexGrowFactor;\n var/*float*/ baseMainSize;\n var/*float*/ boundMainSize;\n \n // Do two passes over the flex items to figure out how to distribute the remaining space.\n // The first pass finds the items whose min/max constraints trigger, freezes them at those\n // sizes, and excludes those sizes from the remaining space. The second pass sets the size\n // of each flexible item. It distributes the remaining space amongst the items whose min/max\n // constraints didn't trigger in pass 1. For the other items, it sets their sizes by forcing\n // their min/max constraints to trigger again. \n //\n // This two pass approach for resolving min/max constraints deviates from the spec. The\n // spec (https://www.w3.org/TR/css-flexbox-1/#resolve-flexible-lengths) describes a process\n // that needs to be repeated a variable number of times. The algorithm implemented here\n // won't handle all cases but it was simpler to implement and it mitigates performance\n // concerns because we know exactly how many passes it'll do.\n \n // First pass: detect the flex items whose min/max constraints trigger\n var/*float*/ deltaFlexShrinkScaledFactors = 0;\n var/*float*/ deltaFlexGrowFactors = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexShrinkScaledFactors -= flexShrinkScaledFactor;\n }\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n baseMainSize = childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor;\n boundMainSize = boundAxis(currentRelativeChild, mainAxis, baseMainSize);\n if (baseMainSize !== boundMainSize) {\n // By excluding this item's size and flex factor from remaining, this item's\n // min/max constraints should also trigger in the second pass resulting in the\n // item's size calculation being identical in the first and second passes.\n deltaFreeSpace -= boundMainSize - childFlexBasis;\n deltaFlexGrowFactors -= flexGrowFactor;\n }\n }\n }\n \n currentRelativeChild = currentRelativeChild.nextChild;\n }\n \n totalFlexShrinkScaledFactors += deltaFlexShrinkScaledFactors;\n totalFlexGrowFactors += deltaFlexGrowFactors;\n remainingFreeSpace += deltaFreeSpace;\n \n // Second pass: resolve the sizes of the flexible items\n deltaFreeSpace = 0;\n currentRelativeChild = firstRelativeChild;\n while (currentRelativeChild !== undefined) {\n childFlexBasis = currentRelativeChild.layout.flexBasis;\n var/*float*/ updatedMainSize = childFlexBasis;\n\n if (remainingFreeSpace < 0) {\n flexShrinkScaledFactor = getFlexShrinkFactor(currentRelativeChild) * childFlexBasis;\n \n // Is this child able to shrink?\n if (flexShrinkScaledFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexShrinkScaledFactors * flexShrinkScaledFactor);\n }\n } else if (remainingFreeSpace > 0) {\n flexGrowFactor = getFlexGrowFactor(currentRelativeChild);\n\n // Is this child able to grow?\n if (flexGrowFactor !== 0) {\n updatedMainSize = boundAxis(currentRelativeChild, mainAxis, childFlexBasis +\n remainingFreeSpace / totalFlexGrowFactors * flexGrowFactor);\n }\n }\n \n deltaFreeSpace -= updatedMainSize - childFlexBasis;\n \n if (isMainAxisRow) {\n childWidth = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = availableInnerCrossDim;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childHeight = currentRelativeChild.style.height + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n } else {\n childHeight = updatedMainSize + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_COLUMN);\n childHeightMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n \n if (!isStyleDimDefined(currentRelativeChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = availableInnerCrossDim;\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_AT_MOST;\n } else {\n childWidth = currentRelativeChild.style.width + getMarginAxis(currentRelativeChild, CSS_FLEX_DIRECTION_ROW);\n childWidthMeasureMode = CSS_MEASURE_MODE_EXACTLY;\n }\n }\n \n var/*bool*/ requiresStretchLayout = !isStyleDimDefined(currentRelativeChild, crossAxis) &&\n getAlignItem(node, currentRelativeChild) === CSS_ALIGN_STRETCH;\n\n // Recursively call the layout algorithm for this child with the updated main size.\n layoutNodeInternal(currentRelativeChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, performLayout && !requiresStretchLayout, 'flex');\n\n currentRelativeChild = currentRelativeChild.nextChild;\n }\n }\n \n remainingFreeSpace = originalRemainingFreeSpace + deltaFreeSpace;\n\n // STEP 6: MAIN-AXIS JUSTIFICATION & CROSS-AXIS SIZE DETERMINATION\n\n // At this point, all the children have their dimensions set in the main axis.\n // Their dimensions are also set in the cross axis with the exception of items\n // that are aligned 'stretch'. We need to compute these stretch values and\n // set the final positions.\n\n // If we are using \"at most\" rules in the main axis, we won't distribute\n // any remaining space at this point.\n if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n remainingFreeSpace = 0;\n }\n\n // Use justifyContent to figure out how to allocate the remaining space\n // available in the main axis.\n if (justifyContent !== CSS_JUSTIFY_FLEX_START) {\n if (justifyContent === CSS_JUSTIFY_CENTER) {\n leadingMainDim = remainingFreeSpace / 2;\n } else if (justifyContent === CSS_JUSTIFY_FLEX_END) {\n leadingMainDim = remainingFreeSpace;\n } else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {\n remainingFreeSpace = fmaxf(remainingFreeSpace, 0);\n if (itemsOnLine > 1) {\n betweenMainDim = remainingFreeSpace / (itemsOnLine - 1);\n } else {\n betweenMainDim = 0;\n }\n } else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {\n // Space on the edges is half of the space between elements\n betweenMainDim = remainingFreeSpace / itemsOnLine;\n leadingMainDim = betweenMainDim / 2;\n }\n }\n\n var/*float*/ mainDim = leadingPaddingAndBorderMain + leadingMainDim;\n var/*float*/ crossDim = 0;\n\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&\n isPosDefined(child, leading[mainAxis])) {\n if (performLayout) {\n // In case the child is position absolute and has left/top being\n // defined, we override the position to whatever the user said\n // (and margin/border).\n child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +\n getLeadingBorder(node, mainAxis) +\n getLeadingMargin(child, mainAxis);\n }\n } else {\n if (performLayout) {\n // If the child is position absolute (without top/left) or relative,\n // we put it at the current accumulated offset.\n child.layout[pos[mainAxis]] += mainDim;\n }\n \n // Now that we placed the element, we need to update the variables.\n // We need to do that only for relative elements. Absolute elements\n // do not take part in that phase.\n if (getPositionType(child) === CSS_POSITION_RELATIVE) {\n if (canSkipFlex) {\n // If we skipped the flex step, then we can't rely on the measuredDims because\n // they weren't computed. This means we can't call getDimWithMargin.\n mainDim += betweenMainDim + getMarginAxis(child, mainAxis) + child.layout.flexBasis;\n crossDim = availableInnerCrossDim;\n } else {\n // The main dimension is the sum of all the elements dimension plus\n // the spacing.\n mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);\n \n // The cross dimension is the max of the elements dimension since there\n // can only be one element in that cross dimension.\n crossDim = fmaxf(crossDim, getDimWithMargin(child, crossAxis));\n }\n }\n }\n }\n\n mainDim += trailingPaddingAndBorderMain;\n \n var/*float*/ containerCrossAxis = availableInnerCrossDim;\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED || measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n // Compute the cross axis from the max cross dimension of the children.\n containerCrossAxis = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n \n if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n containerCrossAxis = fminf(containerCrossAxis, availableInnerCrossDim);\n }\n }\n\n // If there's no flex wrap, the cross dimension is defined by the container.\n if (!isNodeFlexWrap && measureModeCrossDim === CSS_MEASURE_MODE_EXACTLY) {\n crossDim = availableInnerCrossDim;\n }\n\n // Clamp to the min/max size specified on the container.\n crossDim = boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross) - paddingAndBorderAxisCross;\n\n // STEP 7: CROSS-AXIS ALIGNMENT\n // We can skip child alignment if we're just measuring the container.\n if (performLayout) {\n for (i = startOfLineIndex; i < endOfLineIndex; ++i) {\n child = node.children[i];\n\n if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {\n // If the child is absolutely positioned and has a top/left/bottom/right\n // set, override all the previously computed positions to set it correctly.\n if (isPosDefined(child, leading[crossAxis])) {\n child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +\n getLeadingBorder(node, crossAxis) +\n getLeadingMargin(child, crossAxis);\n } else {\n child.layout[pos[crossAxis]] = leadingPaddingAndBorderCross +\n getLeadingMargin(child, crossAxis);\n }\n } else {\n var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;\n\n // For a relative children, we're either using alignItems (parent) or\n // alignSelf (child) in order to determine the position in the cross axis\n var/*css_align_t*/ alignItem = getAlignItem(node, child);\n \n // If the child uses align stretch, we need to lay it out one more time, this time\n // forcing the cross-axis size to be the computed cross size for the current line.\n if (alignItem === CSS_ALIGN_STRETCH) {\n childWidth = child.layout.measuredWidth + getMarginAxis(child, CSS_FLEX_DIRECTION_ROW);\n childHeight = child.layout.measuredHeight + getMarginAxis(child, CSS_FLEX_DIRECTION_COLUMN);\n var/*bool*/ isCrossSizeDefinite = false;\n \n if (isMainAxisRow) {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_COLUMN);\n childHeight = crossDim;\n } else {\n isCrossSizeDefinite = isStyleDimDefined(child, CSS_FLEX_DIRECTION_ROW);\n childWidth = crossDim;\n }\n \n // If the child defines a definite size for its cross axis, there's no need to stretch.\n if (!isCrossSizeDefinite) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n layoutNodeInternal(child, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, true, 'stretch');\n }\n } else if (alignItem !== CSS_ALIGN_FLEX_START) {\n var/*float*/ remainingCrossDim = containerCrossAxis - getDimWithMargin(child, crossAxis);\n\n if (alignItem === CSS_ALIGN_CENTER) {\n leadingCrossDim += remainingCrossDim / 2;\n } else { // CSS_ALIGN_FLEX_END\n leadingCrossDim += remainingCrossDim;\n }\n }\n\n // And we apply the position\n child.layout[pos[crossAxis]] += totalLineCrossDim + leadingCrossDim;\n }\n }\n }\n\n totalLineCrossDim += crossDim;\n maxLineMainDim = fmaxf(maxLineMainDim, mainDim);\n\n // Reset variables for new line.\n lineCount++;\n startOfLineIndex = endOfLineIndex;\n endOfLineIndex = startOfLineIndex;\n }\n\n // STEP 8: MULTI-LINE CONTENT ALIGNMENT\n if (lineCount > 1 && performLayout && !isUndefined(availableInnerCrossDim)) {\n var/*float*/ remainingAlignContentDim = availableInnerCrossDim - totalLineCrossDim;\n\n var/*float*/ crossDimLead = 0;\n var/*float*/ currentLead = leadingPaddingAndBorderCross;\n\n var/*css_align_t*/ alignContent = getAlignContent(node);\n if (alignContent === CSS_ALIGN_FLEX_END) {\n currentLead += remainingAlignContentDim;\n } else if (alignContent === CSS_ALIGN_CENTER) {\n currentLead += remainingAlignContentDim / 2;\n } else if (alignContent === CSS_ALIGN_STRETCH) {\n if (availableInnerCrossDim > totalLineCrossDim) {\n crossDimLead = (remainingAlignContentDim / lineCount);\n }\n }\n\n var/*int*/ endIndex = 0;\n for (i = 0; i < lineCount; ++i) {\n var/*int*/ startIndex = endIndex;\n var/*int*/ j;\n\n // compute the line's height and find the endIndex\n var/*float*/ lineHeight = 0;\n for (j = startIndex; j < childCount; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n if (child.lineIndex !== i) {\n break;\n }\n if (isLayoutDimDefined(child, crossAxis)) {\n lineHeight = fmaxf(lineHeight,\n child.layout[measuredDim[crossAxis]] + getMarginAxis(child, crossAxis));\n }\n }\n endIndex = j;\n lineHeight += crossDimLead;\n\n if (performLayout) {\n for (j = startIndex; j < endIndex; ++j) {\n child = node.children[j];\n if (getPositionType(child) !== CSS_POSITION_RELATIVE) {\n continue;\n }\n\n var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);\n if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n } else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {\n child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[measuredDim[crossAxis]];\n } else if (alignContentAlignItem === CSS_ALIGN_CENTER) {\n childHeight = child.layout[measuredDim[crossAxis]];\n child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;\n } else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {\n child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);\n // TODO(prenaux): Correctly set the height of items with indefinite\n // (auto) crossAxis dimension.\n }\n }\n }\n\n currentLead += lineHeight;\n }\n }\n\n // STEP 9: COMPUTING FINAL DIMENSIONS\n node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow);\n node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn);\n\n // If the user didn't specify a width or height for the node, set the\n // dimensions based on the children.\n if (measureModeMainDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[mainAxis]] = boundAxis(node, mainAxis, maxLineMainDim);\n } else if (measureModeMainDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[mainAxis]] = fmaxf(\n fminf(availableInnerMainDim + paddingAndBorderAxisMain,\n boundAxisWithinMinAndMax(node, mainAxis, maxLineMainDim)),\n paddingAndBorderAxisMain);\n }\n\n if (measureModeCrossDim === CSS_MEASURE_MODE_UNDEFINED) {\n // Clamp the size to the min/max size, if specified, and make sure it\n // doesn't go below the padding and border amount.\n node.layout[measuredDim[crossAxis]] = boundAxis(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross);\n } else if (measureModeCrossDim === CSS_MEASURE_MODE_AT_MOST) {\n node.layout[measuredDim[crossAxis]] = fmaxf(\n fminf(availableInnerCrossDim + paddingAndBorderAxisCross,\n boundAxisWithinMinAndMax(node, crossAxis, totalLineCrossDim + paddingAndBorderAxisCross)),\n paddingAndBorderAxisCross);\n }\n \n // STEP 10: SETTING TRAILING POSITIONS FOR CHILDREN\n if (performLayout) {\n var/*bool*/ needsMainTrailingPos = false;\n var/*bool*/ needsCrossTrailingPos = false;\n\n if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsMainTrailingPos = true;\n }\n\n if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||\n crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {\n needsCrossTrailingPos = true;\n }\n\n // Set trailing position if necessary.\n if (needsMainTrailingPos || needsCrossTrailingPos) {\n for (i = 0; i < childCount; ++i) {\n child = node.children[i];\n\n if (needsMainTrailingPos) {\n setTrailingPosition(node, child, mainAxis);\n }\n\n if (needsCrossTrailingPos) {\n setTrailingPosition(node, child, crossAxis);\n }\n }\n }\n }\n \n // STEP 11: SIZING AND POSITIONING ABSOLUTE CHILDREN\n currentAbsoluteChild = firstAbsoluteChild;\n while (currentAbsoluteChild !== undefined) {\n // Now that we know the bounds of the container, perform layout again on the\n // absolutely-positioned children.\n if (performLayout) {\n\n childWidth = CSS_UNDEFINED;\n childHeight = CSS_UNDEFINED;\n\n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW)) {\n childWidth = currentAbsoluteChild.style.width + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n } else {\n // If the child doesn't have a specified width, compute the width based on the left/right offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_LEFT) && isPosDefined(currentAbsoluteChild, CSS_RIGHT)) {\n childWidth = node.layout.measuredWidth -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_ROW) + getTrailingBorder(node, CSS_FLEX_DIRECTION_ROW)) -\n (currentAbsoluteChild.style[CSS_LEFT] + currentAbsoluteChild.style[CSS_RIGHT]);\n childWidth = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW, childWidth);\n }\n }\n \n if (isStyleDimDefined(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN)) {\n childHeight = currentAbsoluteChild.style.height + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n } else {\n // If the child doesn't have a specified height, compute the height based on the top/bottom offsets if they're defined.\n if (isPosDefined(currentAbsoluteChild, CSS_TOP) && isPosDefined(currentAbsoluteChild, CSS_BOTTOM)) {\n childHeight = node.layout.measuredHeight -\n (getLeadingBorder(node, CSS_FLEX_DIRECTION_COLUMN) + getTrailingBorder(node, CSS_FLEX_DIRECTION_COLUMN)) -\n (currentAbsoluteChild.style[CSS_TOP] + currentAbsoluteChild.style[CSS_BOTTOM]);\n childHeight = boundAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN, childHeight);\n }\n }\n\n // If we're still missing one or the other dimension, measure the content.\n if (isUndefined(childWidth) || isUndefined(childHeight)) {\n childWidthMeasureMode = isUndefined(childWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n childHeightMeasureMode = isUndefined(childHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n // According to the spec, if the main size is not definite and the\n // child's inline axis is parallel to the main axis (i.e. it's\n // horizontal), the child should be sized using \"UNDEFINED\" in\n // the main size. Otherwise use \"AT_MOST\" in the cross axis.\n if (!isMainAxisRow && isUndefined(childWidth) && !isUndefined(availableInnerWidth)) {\n childWidth = availableInnerWidth;\n childWidthMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n\n // The W3C spec doesn't say anything about the 'overflow' property,\n // but all major browsers appear to implement the following logic.\n if (getOverflow(node) === CSS_OVERFLOW_HIDDEN) {\n if (isMainAxisRow && isUndefined(childHeight) && !isUndefined(availableInnerHeight)) {\n childHeight = availableInnerHeight;\n childHeightMeasureMode = CSS_MEASURE_MODE_AT_MOST;\n }\n }\n\n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, childWidthMeasureMode, childHeightMeasureMode, false, 'abs-measure');\n childWidth = currentAbsoluteChild.layout.measuredWidth + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_ROW);\n childHeight = currentAbsoluteChild.layout.measuredHeight + getMarginAxis(currentAbsoluteChild, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n layoutNodeInternal(currentAbsoluteChild, childWidth, childHeight, direction, CSS_MEASURE_MODE_EXACTLY, CSS_MEASURE_MODE_EXACTLY, true, 'abs-layout');\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_ROW])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_ROW]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_ROW]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_ROW]);\n }\n \n if (isPosDefined(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]) &&\n !isPosDefined(currentAbsoluteChild, leading[CSS_FLEX_DIRECTION_COLUMN])) {\n currentAbsoluteChild.layout[leading[CSS_FLEX_DIRECTION_COLUMN]] =\n node.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n currentAbsoluteChild.layout[measuredDim[CSS_FLEX_DIRECTION_COLUMN]] -\n getPosition(currentAbsoluteChild, trailing[CSS_FLEX_DIRECTION_COLUMN]);\n }\n }\n\n currentAbsoluteChild = currentAbsoluteChild.nextChild;\n }\n }\n \n function canUseCachedMeasurement(availableWidth, availableHeight,\n marginRow, marginColumn,\n widthMeasureMode, heightMeasureMode,\n cachedLayout) {\n\n // Is it an exact match?\n if (cachedLayout.availableWidth === availableWidth &&\n cachedLayout.availableHeight === availableHeight &&\n cachedLayout.widthMeasureMode === widthMeasureMode &&\n cachedLayout.heightMeasureMode === heightMeasureMode) {\n return true;\n }\n \n // If the width is an exact match, try a fuzzy match on the height.\n if (cachedLayout.availableWidth === availableWidth &&\n cachedLayout.widthMeasureMode === widthMeasureMode &&\n heightMeasureMode === CSS_MEASURE_MODE_EXACTLY &&\n availableHeight - marginColumn === cachedLayout.computedHeight) {\n return true;\n }\n \n // If the height is an exact match, try a fuzzy match on the width.\n if (cachedLayout.availableHeight === availableHeight &&\n cachedLayout.heightMeasureMode === heightMeasureMode &&\n widthMeasureMode === CSS_MEASURE_MODE_EXACTLY &&\n availableWidth - marginRow === cachedLayout.computedWidth) {\n return true;\n }\n\n return false;\n }\n \n //\n // This is a wrapper around the layoutNodeImpl function. It determines\n // whether the layout request is redundant and can be skipped.\n //\n // Parameters:\n // Input parameters are the same as layoutNodeImpl (see above)\n // Return parameter is true if layout was performed, false if skipped\n //\n function layoutNodeInternal(node, availableWidth, availableHeight, parentDirection,\n widthMeasureMode, heightMeasureMode, performLayout, reason) {\n var layout = node.layout;\n\n var needToVisitNode = (node.isDirty && layout.generationCount !== gCurrentGenerationCount) ||\n layout.lastParentDirection !== parentDirection;\n\n if (needToVisitNode) {\n // Invalidate the cached results.\n if (layout.cachedMeasurements !== undefined) {\n layout.cachedMeasurements = []; \n }\n if (layout.cachedLayout !== undefined) {\n layout.cachedLayout.widthMeasureMode = undefined;\n layout.cachedLayout.heightMeasureMode = undefined;\n }\n }\n \n var i;\n var len;\n var cachedResults;\n \n // Determine whether the results are already cached. We maintain a separate\n // cache for layouts and measurements. A layout operation modifies the positions\n // and dimensions for nodes in the subtree. The algorithm assumes that each node\n // gets layed out a maximum of one time per tree layout, but multiple measurements\n // may be required to resolve all of the flex dimensions.\n // We handle nodes with measure functions specially here because they are the most\n // expensive to measure, so it's worth avoiding redundant measurements if at all possible.\n if (isMeasureDefined(node)) {\n var marginAxisRow = getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n var marginAxisColumn = getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n \n // First, try to use the layout cache.\n if (layout.cachedLayout &&\n canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,\n widthMeasureMode, heightMeasureMode, layout.cachedLayout)) {\n cachedResults = layout.cachedLayout;\n } else if (layout.cachedMeasurements) {\n // Try to use the measurement cache.\n for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (canUseCachedMeasurement(availableWidth, availableHeight, marginAxisRow, marginAxisColumn,\n widthMeasureMode, heightMeasureMode, layout.cachedMeasurements[i])) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n } else if (performLayout) {\n if (layout.cachedLayout &&\n layout.cachedLayout.availableWidth === availableWidth &&\n layout.cachedLayout.availableHeight === availableHeight &&\n layout.cachedLayout.widthMeasureMode === widthMeasureMode &&\n layout.cachedLayout.heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedLayout;\n }\n } else if (layout.cachedMeasurements) {\n for (i = 0, len = layout.cachedMeasurements.length; i < len; i++) {\n if (layout.cachedMeasurements[i].availableWidth === availableWidth &&\n layout.cachedMeasurements[i].availableHeight === availableHeight &&\n layout.cachedMeasurements[i].widthMeasureMode === widthMeasureMode &&\n layout.cachedMeasurements[i].heightMeasureMode === heightMeasureMode) {\n cachedResults = layout.cachedMeasurements[i];\n break;\n }\n }\n }\n \n if (!needToVisitNode && cachedResults !== undefined) {\n layout.measureWidth = cachedResults.computedWidth;\n layout.measureHeight = cachedResults.computedHeight;\n } else {\n layoutNodeImpl(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, performLayout);\n layout.lastParentDirection = parentDirection;\n \n if (cachedResults === undefined) {\n var newCacheEntry;\n if (performLayout) {\n // Use the single layout cache entry.\n if (layout.cachedLayout === undefined) {\n layout.cachedLayout = {};\n }\n newCacheEntry = layout.cachedLayout;\n } else {\n // Allocate a new measurement cache entry.\n if (layout.cachedMeasurements === undefined) {\n layout.cachedMeasurements = [];\n }\n newCacheEntry = {};\n layout.cachedMeasurements.push(newCacheEntry);\n }\n \n newCacheEntry.availableWidth = availableWidth;\n newCacheEntry.availableHeight = availableHeight;\n newCacheEntry.widthMeasureMode = widthMeasureMode;\n newCacheEntry.heightMeasureMode = heightMeasureMode;\n newCacheEntry.computedWidth = layout.measuredWidth;\n newCacheEntry.computedHeight = layout.measuredHeight;\n }\n }\n \n if (performLayout) {\n node.layout.width = node.layout.measuredWidth;\n node.layout.height = node.layout.measuredHeight;\n layout.shouldUpdate = true;\n }\n \n layout.generationCount = gCurrentGenerationCount;\n return (needToVisitNode || cachedResults === undefined);\n }\n \n function layoutNode(node, availableWidth, availableHeight, parentDirection) {\n // Increment the generation count. This will force the recursive routine to visit\n // all dirty nodes at least once. Subsequent visits will be skipped if the input\n // parameters don't change.\n gCurrentGenerationCount++;\n \n // If the caller didn't specify a height/width, use the dimensions\n // specified in the style.\n if (isUndefined(availableWidth) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_ROW)) {\n availableWidth = node.style.width + getMarginAxis(node, CSS_FLEX_DIRECTION_ROW);\n }\n if (isUndefined(availableHeight) && isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {\n availableHeight = node.style.height + getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN);\n }\n \n var widthMeasureMode = isUndefined(availableWidth) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n var heightMeasureMode = isUndefined(availableHeight) ? CSS_MEASURE_MODE_UNDEFINED : CSS_MEASURE_MODE_EXACTLY;\n \n if (layoutNodeInternal(node, availableWidth, availableHeight, parentDirection, widthMeasureMode, heightMeasureMode, true, 'initial')) {\n setPosition(node, node.layout.direction);\n }\n }\n\n return {\n layoutNodeImpl: layoutNodeImpl,\n computeLayout: layoutNode,\n fillNodes: fillNodes,\n canUseCachedMeasurement: canUseCachedMeasurement\n };\n})();\n\n// This module export is only used for the purposes of unit testing this file. When\n// the library is packaged this file is included within css-layout.js which forms\n// the public API.\nif (typeof exports === 'object') {\n module.exports = computeLayout;\n}\n\n\n return function(node) {\n /*eslint-disable */\n // disabling ESLint because this code relies on the above include\n computeLayout.fillNodes(node);\n computeLayout.computeLayout(node);\n /*eslint-enable */\n };\n}));\n"]} \ No newline at end of file diff --git a/src/Layout.c b/src/Layout.c index 4a064512..f43b38da 100644 --- a/src/Layout.c +++ b/src/Layout.c @@ -678,9 +678,9 @@ static void layoutNodeImpl(css_node_t* node, float availableWidth, float availab // Don't bother sizing the text if both dimensions are already defined. node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - } else if (innerWidth <= 0) { + } else if (innerWidth <= 0 || innerHeight <= 0) { - // Don't bother sizing the text if there's no horizontal space. + // Don't bother sizing the text if there's no horizontal or vertical space. node->layout.measured_dimensions[CSS_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); node->layout.measured_dimensions[CSS_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { diff --git a/src/Layout.js b/src/Layout.js index cc07aa08..bb40f609 100755 --- a/src/Layout.js +++ b/src/Layout.js @@ -628,9 +628,9 @@ var computeLayout = (function() { // Don't bother sizing the text if both dimensions are already defined. node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - } else if (innerWidth <= 0) { + } else if (innerWidth <= 0 || innerHeight <= 0) { - // Don't bother sizing the text if there's no horizontal space. + // Don't bother sizing the text if there's no horizontal or vertical space. node.layout.measuredWidth = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); node.layout.measuredHeight = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { diff --git a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs index 551e0fb3..c6cc7c18 100644 --- a/src/csharp/Facebook.CSSLayout/LayoutEngine.cs +++ b/src/csharp/Facebook.CSSLayout/LayoutEngine.cs @@ -562,9 +562,9 @@ namespace Facebook.CSSLayout // Don't bother sizing the text if both dimensions are already defined. node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - } else if (innerWidth <= 0) { + } else if (innerWidth <= 0 || innerHeight <= 0) { - // Don't bother sizing the text if there's no horizontal space. + // Don't bother sizing the text if there's no horizontal or vertical space. node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else { diff --git a/src/java/src/com/facebook/csslayout/LayoutEngine.java b/src/java/src/com/facebook/csslayout/LayoutEngine.java index 280b542a..1b888497 100644 --- a/src/java/src/com/facebook/csslayout/LayoutEngine.java +++ b/src/java/src/com/facebook/csslayout/LayoutEngine.java @@ -518,9 +518,9 @@ public class LayoutEngine { // Don't bother sizing the text if both dimensions are already defined. node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, availableWidth - marginAxisRow); node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, availableHeight - marginAxisColumn); - } else if (innerWidth <= 0) { + } else if (innerWidth <= 0 || innerHeight <= 0) { - // Don't bother sizing the text if there's no horizontal space. + // Don't bother sizing the text if there's no horizontal or vertical space. node.layout.measuredDimensions[DIMENSION_WIDTH] = boundAxis(node, CSS_FLEX_DIRECTION_ROW, 0); node.layout.measuredDimensions[DIMENSION_HEIGHT] = boundAxis(node, CSS_FLEX_DIRECTION_COLUMN, 0); } else {