text:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// in concatenated header, don't include Layout.h it's already at the top
#ifndef CSS_LAYOUT_IMPLEMENTATION
#include "Layout.h"
#endif

#ifdef _MSC_VER
#include <float.h>
#define isnan _isnan

/* define fmaxf if < VC12 */
#if _MSC_VER < 1800
__forceinline const float fmaxf(const float a, const float b) {
  return (a > b) ? a : b;
}
#endif
#endif

bool isUndefined(float value) {
  return isnan(value);
}

static bool eq(float a, float b) {
  if (isUndefined(a)) {
    return isUndefined(b);
  }
  return fabs(a - b) < 0.0001;
}

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;

  // Some of the fields default to undefined and not 0
  node->style.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
  node->style.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;

  node->style.minDimensions[CSS_WIDTH] = CSS_UNDEFINED;
  node->style.minDimensions[CSS_HEIGHT] = CSS_UNDEFINED;

  node->style.maxDimensions[CSS_WIDTH] = CSS_UNDEFINED;
  node->style.maxDimensions[CSS_HEIGHT] = CSS_UNDEFINED;

  node->style.position[CSS_LEFT] = CSS_UNDEFINED;
  node->style.position[CSS_TOP] = CSS_UNDEFINED;
  node->style.position[CSS_RIGHT] = CSS_UNDEFINED;
  node->style.position[CSS_BOTTOM] = CSS_UNDEFINED;

  node->style.margin[CSS_START] = CSS_UNDEFINED;
  node->style.margin[CSS_END] = CSS_UNDEFINED;
  node->style.padding[CSS_START] = CSS_UNDEFINED;
  node->style.padding[CSS_END] = CSS_UNDEFINED;
  node->style.border[CSS_START] = CSS_UNDEFINED;
  node->style.border[CSS_END] = CSS_UNDEFINED;

  node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
  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_direction = (css_direction_t)-1;
  node->layout.should_update = true;
}

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) {
  free(node);
}

static void indent(int n) {
  for (int i = 0; i < n; ++i) {
    printf("  ");
  }
}

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) {
  if (!isnan(number)) {
    printf("%s: %g, ", str, number);
  }
}

static bool four_equal(float four[4]) {
  return
    eq(four[0], four[1]) &&
    eq(four[0], four[2]) &&
    eq(four[0], four[3]);
}

static void print_css_node_rec(
  css_node_t *node,
  css_print_options_t options,
  int level
) {
  indent(level);
  printf("{");

  if (node->print) {
    node->print(node->context);
  }

  if (options & CSS_PRINT_LAYOUT) {
    printf("layout: {");
    printf("width: %g, ", node->layout.dimensions[CSS_WIDTH]);
    printf("height: %g, ", node->layout.dimensions[CSS_HEIGHT]);
    printf("top: %g, ", node->layout.position[CSS_TOP]);
    printf("left: %g", node->layout.position[CSS_LEFT]);
    printf("}, ");
  }

  if (options & CSS_PRINT_STYLE) {
    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', ");
    } 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', ");
    }

    if (node->style.justify_content == CSS_JUSTIFY_CENTER) {
      printf("justifyContent: 'center', ");
    } else if (node->style.justify_content == CSS_JUSTIFY_FLEX_END) {
      printf("justifyContent: 'flex-end', ");
    } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_AROUND) {
      printf("justifyContent: 'space-around', ");
    } else if (node->style.justify_content == CSS_JUSTIFY_SPACE_BETWEEN) {
      printf("justifyContent: 'space-between', ");
    }

    if (node->style.align_items == CSS_ALIGN_CENTER) {
      printf("alignItems: 'center', ");
    } else if (node->style.align_items == CSS_ALIGN_FLEX_END) {
      printf("alignItems: 'flex-end', ");
    } else if (node->style.align_items == CSS_ALIGN_STRETCH) {
      printf("alignItems: 'stretch', ");
    }

    if (node->style.align_content == CSS_ALIGN_CENTER) {
      printf("alignContent: 'center', ");
    } else if (node->style.align_content == CSS_ALIGN_FLEX_END) {
      printf("alignContent: 'flex-end', ");
    } else if (node->style.align_content == CSS_ALIGN_STRETCH) {
      printf("alignContent: 'stretch', ");
    }

    if (node->style.align_self == CSS_ALIGN_FLEX_START) {
      printf("alignSelf: 'flex-start', ");
    } else if (node->style.align_self == CSS_ALIGN_CENTER) {
      printf("alignSelf: 'center', ");
    } else if (node->style.align_self == CSS_ALIGN_FLEX_END) {
      printf("alignSelf: 'flex-end', ");
    } else if (node->style.align_self == CSS_ALIGN_STRETCH) {
      printf("alignSelf: 'stretch', ");
    }

    print_number_nan("flex", node->style.flex);

    if (four_equal(node->style.margin)) {
      print_number_0("margin", node->style.margin[CSS_LEFT]);
    } else {
      print_number_0("marginLeft", node->style.margin[CSS_LEFT]);
      print_number_0("marginRight", node->style.margin[CSS_RIGHT]);
      print_number_0("marginTop", node->style.margin[CSS_TOP]);
      print_number_0("marginBottom", node->style.margin[CSS_BOTTOM]);
      print_number_0("marginStart", node->style.margin[CSS_START]);
      print_number_0("marginEnd", node->style.margin[CSS_END]);
    }

    if (four_equal(node->style.padding)) {
      print_number_0("padding", node->style.margin[CSS_LEFT]);
    } else {
      print_number_0("paddingLeft", node->style.padding[CSS_LEFT]);
      print_number_0("paddingRight", node->style.padding[CSS_RIGHT]);
      print_number_0("paddingTop", node->style.padding[CSS_TOP]);
      print_number_0("paddingBottom", node->style.padding[CSS_BOTTOM]);
      print_number_0("paddingStart", node->style.padding[CSS_START]);
      print_number_0("paddingEnd", node->style.padding[CSS_END]);
    }

    if (four_equal(node->style.border)) {
      print_number_0("borderWidth", node->style.border[CSS_LEFT]);
    } else {
      print_number_0("borderLeftWidth", node->style.border[CSS_LEFT]);
      print_number_0("borderRightWidth", node->style.border[CSS_RIGHT]);
      print_number_0("borderTopWidth", node->style.border[CSS_TOP]);
      print_number_0("borderBottomWidth", node->style.border[CSS_BOTTOM]);
      print_number_0("borderStartWidth", node->style.border[CSS_START]);
      print_number_0("borderEndWidth", node->style.border[CSS_END]);
    }

    print_number_nan("width", node->style.dimensions[CSS_WIDTH]);
    print_number_nan("height", node->style.dimensions[CSS_HEIGHT]);

    if (node->style.position_type == CSS_POSITION_ABSOLUTE) {
      printf("position: 'absolute', ");
    }

    print_number_nan("left", node->style.position[CSS_LEFT]);
    print_number_nan("right", node->style.position[CSS_RIGHT]);
    print_number_nan("top", node->style.position[CSS_TOP]);
    print_number_nan("bottom", node->style.position[CSS_BOTTOM]);
  }

  if (options & CSS_PRINT_CHILDREN && node->children_count > 0) {
    printf("children: [\n");
    for (int i = 0; i < node->children_count; ++i) {
      print_css_node_rec(node->get_child(node->context, i), options, level + 1);
    }
    indent(level);
    printf("]},\n");
  } else {
    printf("},\n");
  }
}

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,
  /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT,
  /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT
};
static css_position_t trailing[4] = {
  /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_BOTTOM,
  /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_TOP,
  /* CSS_FLEX_DIRECTION_ROW = */ CSS_RIGHT,
  /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_LEFT
};
static css_position_t pos[4] = {
  /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_TOP,
  /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_BOTTOM,
  /* CSS_FLEX_DIRECTION_ROW = */ CSS_LEFT,
  /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_RIGHT
};
static css_dimension_t dim[4] = {
  /* CSS_FLEX_DIRECTION_COLUMN = */ CSS_HEIGHT,
  /* CSS_FLEX_DIRECTION_COLUMN_REVERSE = */ CSS_HEIGHT,
  /* CSS_FLEX_DIRECTION_ROW = */ CSS_WIDTH,
  /* CSS_FLEX_DIRECTION_ROW_REVERSE = */ CSS_WIDTH
};

static bool isRowDirection(css_flex_direction_t flex_direction) {
  return flex_direction == CSS_FLEX_DIRECTION_ROW ||
         flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE;
}

static bool isColumnDirection(css_flex_direction_t flex_direction) {
  return flex_direction == CSS_FLEX_DIRECTION_COLUMN ||
         flex_direction == CSS_FLEX_DIRECTION_COLUMN_REVERSE;
}

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];
  }

  return node->style.margin[leading[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];
  }

  return node->style.margin[trailing[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) {
    return node->style.padding[CSS_START];
  }

  if (node->style.padding[leading[axis]] >= 0) {
    return node->style.padding[leading[axis]];
  }

  return 0;
}

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) {
    return node->style.padding[CSS_END];
  }

  if (node->style.padding[trailing[axis]] >= 0) {
    return node->style.padding[trailing[axis]];
  }

  return 0;
}

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) {
    return node->style.border[CSS_START];
  }

  if (node->style.border[leading[axis]] >= 0) {
    return node->style.border[leading[axis]];
  }

  return 0;
}

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) {
    return node->style.border[CSS_END];
  }

  if (node->style.border[trailing[axis]] >= 0) {
    return node->style.border[trailing[axis]];
  }

  return 0;
}

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) {
  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) {
  return getLeadingMargin(node, axis) + getTrailingMargin(node, 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) {
  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) {
  css_direction_t direction = node->style.direction;

  if (direction == CSS_DIRECTION_INHERIT) {
    direction = parentDirection > CSS_DIRECTION_INHERIT ? parentDirection : CSS_DIRECTION_LTR;
  }

  return direction;
}

static css_flex_direction_t getFlexDirection(css_node_t *node) {
  return node->style.flex_direction;
}

static css_flex_direction_t resolveAxis(css_flex_direction_t flex_direction, css_direction_t direction) {
  if (direction == CSS_DIRECTION_RTL) {
    if (flex_direction == CSS_FLEX_DIRECTION_ROW) {
      return CSS_FLEX_DIRECTION_ROW_REVERSE;
    } else if (flex_direction == CSS_FLEX_DIRECTION_ROW_REVERSE) {
      return CSS_FLEX_DIRECTION_ROW;
    }
  }

  return flex_direction;
}

static css_flex_direction_t getCrossFlexDirection(css_flex_direction_t flex_direction, css_direction_t direction) {
  if (isColumnDirection(flex_direction)) {
    return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
  } else {
    return CSS_FLEX_DIRECTION_COLUMN;
  }
}

static float getFlex(css_node_t *node) {
  return node->style.flex;
}

static bool isFlex(css_node_t *node) {
  return (
    node->style.position_type == CSS_POSITION_RELATIVE &&
    getFlex(node) > 0
  );
}

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]] +
    getLeadingMargin(node, axis) +
    getTrailingMargin(node, axis);
}

static bool isDimDefined(css_node_t *node, css_flex_direction_t axis) {
  float value = node->style.dimensions[dim[axis]];
  return !isUndefined(value) && value >= 0.0;
}

static bool isPosDefined(css_node_t *node, css_position_t position) {
  return !isUndefined(node->style.position[position]);
}

static bool isMeasureDefined(css_node_t *node) {
  return node->measure;
}

static float getPosition(css_node_t *node, css_position_t position) {
  float result = node->style.position[position];
  if (!isUndefined(result)) {
    return result;
  }
  return 0;
}

static float boundAxis(css_node_t *node, css_flex_direction_t axis, float value) {
  float min = CSS_UNDEFINED;
  float max = CSS_UNDEFINED;

  if (isColumnDirection(axis)) {
    min = node->style.minDimensions[CSS_HEIGHT];
    max = node->style.maxDimensions[CSS_HEIGHT];
  } else if (isRowDirection(axis)) {
    min = node->style.minDimensions[CSS_WIDTH];
    max = node->style.maxDimensions[CSS_WIDTH];
  }

  float boundValue = value;

  if (!isUndefined(max) && max >= 0.0 && boundValue > max) {
    boundValue = max;
  }
  if (!isUndefined(min) && min >= 0.0 && boundValue < min) {
    boundValue = min;
  }

  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 (!isUndefined(node->layout.dimensions[dim[axis]])) {
    return;
  }
  // We only run if there's a width or height defined
  if (!isDimDefined(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)
  );
}

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]];
  }

// 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) {
  float lead = node->style.position[leading[axis]];
  if (!isUndefined(lead)) {
    return lead;
  }
  return -getPosition(node, trailing[axis]);
}

static void layoutNodeImpl(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) {
  /** START_GENERATED **/
  css_direction_t direction = resolveDirection(node, parentDirection);
  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);

  // 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]] += 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);

  if (isMeasureDefined(node)) {
    bool isResolvedRowDimDefined = !isUndefined(node->layout.dimensions[dim[resolvedRowAxis]]);

    float width = CSS_UNDEFINED;
    if (isDimDefined(node, resolvedRowAxis)) {
      width = node->style.dimensions[CSS_WIDTH];
    } else if (isResolvedRowDimDefined) {
      width = node->layout.dimensions[dim[resolvedRowAxis]];
    } else {
      width = parentMaxWidth -
        getMarginAxis(node, resolvedRowAxis);
    }
    width -= paddingAndBorderAxisResolvedRow;

    // 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 = !isDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;
    bool isColumnUndefined = !isDimDefined(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) {
      css_dim_t measureDim = node->measure(
        node->context,

        width
      );
      if (isRowUndefined) {
        node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
          paddingAndBorderAxisResolvedRow;
      }
      if (isColumnUndefined) {
        node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
          getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
      }
    }
    if (childCount == 0) {
      return;
    }
  }

  bool isNodeFlexWrap = isFlexWrap(node);

  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 = !isUndefined(node->layout.dimensions[dim[mainAxis]]);
  bool isCrossDimDefined = !isUndefined(node->layout.dimensions[dim[crossAxis]]);
  bool isMainRowDirection = isRowDirection(mainAxis);

  int i;
  int ii;
  css_node_t* child;
  css_flex_direction_t axis;

  css_node_t* firstAbsoluteChild = NULL;
  css_node_t* currentAbsoluteChild = NULL;

  float definedMainDim = CSS_UNDEFINED;
  if (isMainDimDefined) {
    definedMainDim = node->layout.dimensions[dim[mainAxis]] - paddingAndBorderAxisMain;
  }

  // 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) {
    // <Loop A> Layout non flexible children and count children by type

    // 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;

    // 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;

    // 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 <Loop C>.
    bool isSimpleStackMain =
        (isMainDimDefined && justifyContent == CSS_JUSTIFY_FLEX_START) ||
        (!isMainDimDefined && justifyContent != CSS_JUSTIFY_CENTER);
    int firstComplexMain = (isSimpleStackMain ? childCount : startLine);

    // 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 <Loop D>.
    bool isSimpleStackCross = true;
    int firstComplexCross = childCount;

    css_node_t* firstFlexChild = NULL;
    css_node_t* currentFlexChild = NULL;

    float mainDim = leadingPaddingAndBorderMain;
    float crossDim = 0;

    float maxWidth;
    for (i = startLine; i < childCount; ++i) {
      child = node->get_child(node->context, i);
      child->line_index = linesCount;

      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 &&
          !isDimDefined(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 (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 (!isUndefined(node->layout.dimensions[dim[axis]]) &&
              !isDimDefined(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)
            );
          }
        }
      }

      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;
        if (!isMainRowDirection) {
          if (isDimDefined(node, resolvedRowAxis)) {
            maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
              paddingAndBorderAxisResolvedRow;
          } else {
            maxWidth = parentMaxWidth -
              getMarginAxis(node, resolvedRowAxis) -
              paddingAndBorderAxisResolvedRow;
          }
        }

        // This is the main recursive call. We layout non flexible children.
        if (alreadyComputedNextLayout == 0) {
          layoutNode(child, maxWidth, 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 <Loop C>.
      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 <Loop D>.
      if (isSimpleStackCross &&
          (child->style.position_type != CSS_POSITION_RELATIVE ||
              (alignItem != CSS_ALIGN_STRETCH && alignItem != CSS_ALIGN_FLEX_START) ||
              isUndefined(child->layout.dimensions[dim[crossAxis]]))) {
        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;
    }

    // <Loop B> Layout flexible children and allocate empty space

    // In order to position the elements in the main axis, we have two
    // controls. The space between the beginning and the first element
    // and the space between each two elements.
    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;
    }

    // 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 (isDimDefined(node, resolvedRowAxis)) {
          maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
            paddingAndBorderAxisResolvedRow;
        } else if (!isMainRowDirection) {
          maxWidth = parentMaxWidth -
            getMarginAxis(node, resolvedRowAxis) -
            paddingAndBorderAxisResolvedRow;
        }

        // And we recursively call the layout algorithm for this child
        layoutNode(currentFlexChild, maxWidth, 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) {
      if (justifyContent == CSS_JUSTIFY_CENTER) {
        leadingMainDim = remainingMainDim / 2;
      } else if (justifyContent == CSS_JUSTIFY_FLEX_END) {
        leadingMainDim = remainingMainDim;
      } else if (justifyContent == CSS_JUSTIFY_SPACE_BETWEEN) {
        remainingMainDim = fmaxf(remainingMainDim, 0);
        if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 != 0) {
          betweenMainDim = remainingMainDim /
            (flexibleChildrenCount + nonFlexibleChildrenCount - 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);
        leadingMainDim = betweenMainDim / 2;
      }
    }

    // <Loop C> Position elements in the main axis and compute dimensions

    // 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) {
      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);
        }

        // Now that we placed the element, we need to update the variables
        // We only need to do that 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)));
        }
      }
    }

    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
      );
    }

    // <Loop D> Position elements in the cross axis
    for (i = firstComplexCross; i < endLine; ++i) {
      child = node->get_child(node->context, i);

      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);

      } 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
        if (child->style.position_type == CSS_POSITION_RELATIVE) {
          css_align_t alignItem = getAlignItem(node, child);
          if (alignItem == CSS_ALIGN_STRETCH) {
            // You can only stretch if the dimension has not already been set
            // previously.
            if (!isDimDefined(child, 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)
              );
            }
          } 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);

            if (alignItem == CSS_ALIGN_CENTER) {
              leadingCrossDim += remainingCrossDim / 2;
            } else { // CSS_ALIGN_FLEX_END
              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);
        }
      }
    }

    linesCrossDim += crossDim;
    linesMainDim = fmaxf(linesMainDim, mainDim);
    linesCount += 1;
    startLine = endLine;
  }

  // <Loop E>
  //
  // Note(prenaux): More than one line, we need to layout the crossAxis
  // according to alignContent.
  //
  // Note that we could probably remove <Loop D> 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;

    float crossDimLead = 0;
    float currentLead = leadingPaddingAndBorderCross;

    css_align_t alignContent = node->style.align_content;
    if (alignContent == CSS_ALIGN_FLEX_END) {
      currentLead += remainingAlignContentDim;
    } else if (alignContent == CSS_ALIGN_CENTER) {
      currentLead += remainingAlignContentDim / 2;
    } else if (alignContent == CSS_ALIGN_STRETCH) {
      if (nodeCrossAxisInnerSize > linesCrossDim) {
        crossDimLead = (remainingAlignContentDim / linesCount);
      }
    }

    int endIndex = 0;
    for (i = 0; i < linesCount; ++i) {
      int startIndex = endIndex;

      // 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);
        if (child->style.position_type != CSS_POSITION_RELATIVE) {
          continue;
        }
        if (child->line_index != i) {
          break;
        }
        if (!isUndefined(child->layout.dimensions[dim[crossAxis]])) {
          lineHeight = fmaxf(
            lineHeight,
            child->layout.dimensions[dim[crossAxis]] + getMarginAxis(child, crossAxis)
          );
        }
      }
      endIndex = ii;
      lineHeight += crossDimLead;

      for (ii = startIndex; ii < endIndex; ++ii) {
        child = node->get_child(node->context, ii);
        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.
        }
      }

      currentLead += lineHeight;
    }
  }

  bool needsMainTrailingPos = false;
  bool needsCrossTrailingPos = false;

  // 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 (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;
    }
  }

  // <Loop F> 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 (needsCrossTrailingPos) {
        setTrailingPosition(node, child, crossAxis);
      }
    }
  }

  // <Loop G> Calculate dimensions for absolutely positioned elements
  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;

      if (!isUndefined(node->layout.dimensions[dim[axis]]) &&
          !isDimDefined(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)
        );
      }

      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]);
      }
    }

    child = currentAbsoluteChild;
    currentAbsoluteChild = currentAbsoluteChild->next_absolute_child;
    child->next_absolute_child = NULL;
  }
  /** END_GENERATED **/
}

void layoutNode(css_node_t *node, float parentMaxWidth, css_direction_t parentDirection) {
  css_layout_t *layout = &node->layout;
  css_direction_t direction = node->style.direction;
  layout->should_update = true;

  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_direction, direction);

  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];
  } 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_direction = direction;

    layoutNodeImpl(node, parentMaxWidth, parentDirection);

    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];
  }
}


text:
Home Entertainment Movie Reviews Waltz With Bashir Movie Review: Dances With Uzis
Waltz With Bashir Movie Review: Dances With Uzis
Fri January 2, 2009
An Israeli anti-war feature that is as gravely concerned with the way battle is experienced in the head, as via guns and carnage, Waltz With Bashir simultaneously blasts its way into theaters and through the minds of audiences – in animation. An Israeli Apocalpyse Now, Waltz With Bashir is, much like Oliver Stone's Platoon, a profoundly subjective and visceral account of war trauma, similarly through the eyes and damaged psyche of another combat vet turned filmmaker, Ari Folman.
The Israeli director is less focused on introducing his story, than unleashing it, intent on thrusting that war experience on viewers just as he encountered it as a teen soldier inducted into the 1982 Lebanon invasion, who had barely begun to shave. And more in the tradition of horror than docudrama, the film opens with a pack of wild snarling attack dogs tearing through the city streets, hungry for human flesh. We learn that these terrifying images are the first of a series of post-traumatic anguished nightmares experienced by Ari's mentally damaged comrade vets.
Playing his both younger and present self in animation, Ari struggles with a very different kind of psychological war wound – a complete failure of memory. Yet Ari knows that he was present during the horrific mass slaughter of Palestinian civilians at the Sabra and Shatila refugee camps in Lebanon back then, so who is the memory thief, and why can he not achieve any peace of mind unless he wrestles triumphantly with his war demons. These troubling questions take viewers along on a breathlessly surreal journey through fictionalized recurrent personal nightmares and agonizing flashback collective memory, both real and imagined, that is shared with fellow Israeli vets. Including that Beirut urban warfare waltz, a dance of death with machine gun blazing, of one soldier on the brink of insanity.
The film does leave unanswered the many questions about the actual complicit role of Israel in the Palestinian civilian massacre by the right wing Lebanese Christian Phalangists after the assassination of their idolized leader Bashir Gemayel (carried out by one of their own). And the uninvestigated suspicions of a related Israeli secret stadium slaughter and mass burial of many of these refugees.
But Folman, a child of Auschwitz Holocaust survivors, doesn't flinch from the images of the young detained men with crucifixes carved into their chests by the Phalangists, or the terrified children, women and elderly remaining survivors with their hands in the air. And the gnawing self-accusation – am I now the Nazi? An unspeakable genocide of his own generation put into further deplorable perspective, as then Israeli leader Ariel Sharon receives an emergency telephone call to halt the carnage and simply replies before going back to sleep, 'Thank you, and Happy New Year.'
Waltz With Bashir is that rare feature that is equally the best film of the year, and the most important. Folman's intent here is clearly to open up a second front, so to speak, spotlighting that other victim of war – the youth sent off to die in combat or left permanently scarred, and barely comprehending the bloody mission thrust upon them. And Folman asks for neither sadness nor pity, but rather the audience's ignited rage, as helplessly complicit as he has been, in the pain and horror perpetrated and endured.
Waltz With Bashir Movie

redpajama_set_name: RedPajamaCommonCrawl


A former convict in South Africa has turned his life around - through music. William Bapela spent five years in jail, but emerged with a mission. Now he's helping children in one of the country's poorest suburbs build a future for themselves. CCTV's Julie Scheier has the story.


text:
Plymouth had …. uh … let's say "nerve." The company's resounding message in the case of the Hemi 'Cuda was: "Damn the insurance company torpedos— full speed ahead!" The car tested out like a rocketship, as you'll see in the performance figures at the end of this essay. As Chrysler maven Cliff Gromer's Mopar Muscle magazine put it, "The new E-bodies offered a home for ol' King Kong hisself—the 426 Hemi."
When the redesigned 1970 Plymouth Barracuda came to the muscle-car market, there would be no excuses for not putting a big engine in the gaping crater under its wide hood. Design engineers had stretched the car sideways by more than 5 inches and increased both the front and rear tracks by 3 inches. As a result, any Chrysler Corporation engine would fit in the engine bay, right up to the street version of the "Monster Masher" racing power plant—the 426-cid Hemi.
The Hemi was an $871.45 option for the muscular 'Cuda sport coupe (which was base priced at $3,164) and the convertible (which carried a $3,433 window sticker total). The 'Cuda came standard with another big-block mill—the 383-cid/355-hp V-8. No wonder Chrysler listed the 'Cuda as a member of its "Rapid Transit System."
Totally redesigned, the 1970 Barracuda offered buyers a wide menu off models and engines. Three styles were available with a long list of engines. The specialty performance class 'Cuda' offered an innovative 'shaker' hood option. A classy Gran Coupe was the top-of-the-line. Even the 426-cid 'Street Hemi' was now offered in special Barracudas, built in limited numbers. Standard equipment in Barracudas included high-back bucket seats with all-vinyl trim; integral head rests; molded door and quarter trim panels; flood-111 instrument panel; three-spoke woodgrain steering wheel; floor shift; carpeting; standard 225-cid six and E78-15 fiberglass-belted tires. The Gran Coupe had all Barracuda items plus body sill, wheel lip, and beltline moldings; Gran Coupe emblems; leather bucket seats and consolette in Knit Jersey (hardtop only); molded headliner; and in convertible, leather bucket seats; DeLuxe vinyl or cloth and vinyl trims were available at lowered cost. The 'Cuda' carried all Gran Coupe equipment plus the four-barrel '383' V-8; heavy-duty suspension and drum brakes; 'Cuda' ornamentation; and F70-14 raised white letter tires.
Street Hemis got new hydraulic valve lifters for 1970, but a new cam profile gave the Mopar engineers no reason to alter the 425 advertised hp rating. The Hemi's two Carter AFB four-barrel carburetors breathed through the Air Grabber "shaker" hood scoop.
In order to get the horses to the pavement, Hemi-powered 'Cudas and other big-engined Barracudas relied on heavy-duty drive-line parts. There was a choice of the New Process A-833 four-speed manual gearbox or the 727 TorqueFlite automatic. A Dana 9 3/4-inch differential was kept in place by a leaf-spring rear suspension with six leaves on the right and five leaves plus two half-leaves on the left. Fifteen-inch-diameter, 7-inch-wide wheels held F60 x 15 tires.
In short, power was the Hemi 'Cuda's long suit. Not long was the list of buyers. Insurance companies did not look kindly on Hemi 'Cudas and did not care if they could do 0-to-60 mph in 5.8 seconds and run down the quarter-mile in 14.1 seconds at 103.2 mph.
Hemi Cudas, particularly the convertibles, definitely fall into the "Holy Grail" category when it comes to American muscle cars. Only 652 1970 Hemi Cuda hardtops were built, and just 14 convertibles were sold in the U.S. market (three were also built for sale in Canada, and one other convertible was sent overseas).
Collector Pricing
The 1970 Hemi 'Cuda coupes have been grabbed at auction in recent years for as little as $150,000. Others have gone for much more — although the 1971 Hemi Cudas are even rarer and more expensive. The sky is the limit for '70 Cuda convertibles prices. An East Coast collector has the first '70 ragtop off the assembly line and some estimates have put its market value in the $5 million range.
While the Hemi craze has undergone a bit of a market correction as of late, the 1970 Hemi 'Cuda still attracts top dollar when offered. Rare enough in authentic form, this car has spawned many imitators in replica form, and even these garner illogical money. In current market, a pair of No. 2 condition '70 Hemi 'Cuda coupes sold for $270,000 and $227,000 at the recent Mecum Indianapolis sale while a No. 1 example sold for $400,000 at the January Russo and Steele Scottsdale auction.
The Mopar Hemi cars were about as good as it got during the Golden Age of muscle cars, and the 1970 Hemi Cuda was certainly one of fiercest. Today, it is one of the rarest and most coveted.
CLICK HERE to return to the Car of the Week index.
PlymouthBarracuda


I've searched a lot on the Internet (I consider my google-fu quite good) for some well written tutorial about writing simple "RESTful" microservices in Go with Go kit.
Examples from the Go kit repository are good, but documentation is dull, IMHO.
Sooo, I decided to give back something to the community and write a tutorial, in order to "learn by doing". This tutorial will be heavily inspired by the book mentioned above, and probably could be improved in many ways.
Feel free to give a feedback!
You will find the link to the complete tutorial on dev.to.
And here you will find the full code for you to use!


Charles Cleghorn Ltd was formed in October 2007 by Charles Cleghorn as quality workboat equipment specialists. Charles is well known within the marine industry, with 36 years experience sourcing and supplying bronze sea-water pumps and other equipment to coastal marine engineering and workboat building companies throughout the UK and Ireland. His company have been appointed as the UK and Ireland distributor for Northern Marine Electrics' all UK made ventilation fans. These are designed specifically for workboat use. All 24 volt fans now come with brushless motors.
CCLtd has also taken on a highly innovative range of technically advanced under-sea cleaning machines – the Caviblaster by Cavidyne LLC.


text:
Introducing the Sort-of Buddhist: Or, "If There Is No 'I' to Have a Religious Identity, Then How Do I Fill Out This Survey?"
Author: Anne C. Spencer (The College of Idaho Caldwell) and Scott Draper ( The College of Idaho Caldwell)
Western concepts of religious identity developed from worldviews that posit a permanent self and require exclusive acceptance of a single religion for salvation. These assumptions become problematic, however, when applied to traditions holding different worldviews. As a result, Western religious demographic research uses categories that do not accurately reflect many practitioners' understanding of themselves and their religious paths. New approaches are needed to assess contemporary religious identity. A 2011 survey of participants in the Buddhist Churches of America asked respondents, "Would you describe yourself as Buddhist?" Response options included the new category: "yes, sort of." While the majority answered that they were "definitely" Buddhist, 15 percent chose the "sort of" option. We explore potential motives for this selection from multiple perspectives including a brief overview of Buddhist philosophy and teaching regarding the nature of self, a review of previous literature on Buddhist identity, and quantitative and qualitative analysis of new and existing data. Our literature review and qualitative results suggest that the choice of Sort-of Buddhist identity reflects an understanding of religious identity grounded in Buddhist teaching regarding the "self" as impermanent and interdependent. We also identify a pattern in which individuals who began attending Buddhist temples as adults are disproportionately likely to identify as "sort of" Buddhist, even though they respond similarly to "definitely" Buddhists on measures of religious participation. Finally, we suggest that contemporary ambivalence toward exclusive religious identity in the U.S. may also be a factor in choosing a "sort of" religious identity.


text:
U.S. Chamber of Commerce Foundation / North America Knows How to Achieve Zero Waste — the Key Is in Our Infrastructure
North America Knows How to Achieve Zero Waste — the Key Is in Our Infrastructure
Bob Cappadona
architecture-bay-bridge-800px.jpg
Recycling initiatives are nothing new, but companies that depend on efficient hazardous waste management should be keen to the infrastructure that is operating right now and dedicated to those who need to know how to achieve zero waste. At Veolia, this applies to numerous spaces: household materials, the automotive industry, and electronics that don't have a clear future once their useful life has expired.
But businesses looking to strengthen their CSR efforts have some important criteria for what deserves their attention. It should — among other things — be economical, respectful of employee time, and serving of any local compliance needs they may have. Here are three cities that are demonstrating zero waste initiatives, conserving resources, and adhering to today's demanding environmental policy.
In 2016, New York City collected more than 850,000 pounds of hazardous waste for beneficial reuse.
The average American citizen produces 4 pounds of harmful household waste per year. This includes motor oil, pesticides, latex paint, and similar materials whose toxicity can be detrimental to wildlife and local waterways. In New York City, the Department of Sanitation partners with Veolia to host events for the collection of household hazardous products (HHP) in each borough, ensuring individuals know where to discard these materials safely and how to achieve zero waste in the process.
Much of the waste people generate isn't just harmful to the environment when disposed of; it's also more valuable when repurposed. Rather than emptying refuse into the sink, toilet or garbage, New Yorkers can leverage these collection events — as well as local facilities and mail-in programs — that turn this refuse into useable byproducts. Certain contents are even converted to biofuel for industrial operations. This reduces the municipal waste stream, minimizes waste management costs and can be completed in a weekend.
In the Midwest, zero waste has a numerical value: eight million pounds, or the amount of manufacturing waste Ford Motor Company is diverting from the landfill each year.
Indeed, commercial spaces often have more hazardous waste than is manageable at a community collection event. Ford Rouge (and 68 other Ford manufacturing plants worldwide) is a terrific model for how even the most waste-intensive facilities can build a non-invasive management strategy into its daily operations. By making small-scale investments in renewables, such as solar-powered compactors, the company was able to qualify for large waste-to-energy initiatives across the region and make a substantial contribution to the circular economy. The factory declared itself Zero Waste to Landfill in July of 2016.
In Ontario, Canada, landfill dependence is now heavily regulated, banning all lamps from landfills by 2020. Naturally, this pressures homes and businesses to find more cost-efficient methods of waste management that also keeps them compliant. Right now, only 24 percent of the 600 million lamps are recycled per local policy.
With that in mind, a new 5,000-square-foot electronics recycling facility offers opportunities to organizations that need an easy and sustainable solution to these restrictions across Ontario. A replica of facilities operated by Veolia in Massachusetts, Wisconsin, Arizona and Florida, this Pickering-based site accepts mercury-containing electronics nationwide and isolates the glass, metal, and phosphor powder for reuse.
Not only does this ensure organizations remain lawful entering the new decade, but it sets a tremendous bar of convenience for provinces across Canada when looking to conserve resources and make the transition to more energy-efficient lighting. Read more about this facility here and see how fluorescent lamps are recycled below.
Infographic.jpg
Ontario isn't the only region whose environmental policy is extending to more end-users. Cities across North America are aligning their industrial operations with ambitious CO2-reduction goals in the same time frame, and the average enterprise must follow suit. Fortunately, zero waste is within reach — we just need to know where to look.
[Editor's Note: Veolia North America's Ashley Paulsworth, Sustainability Program Manager, as well as Mike Byrnes, Chief Operating Officer, Veolia SourceOne, were featured speakers at the Chamber Foundation's recent 2018 Sustainability and Circular Economy Summit. For more information on the event, click here.]
Innovating Solutions to Address the Sustainable Development Goals
The History of America Recycles Day
How to Navigate Towards a Plastic Pollution-Free Future
Bob Gedert
Ron Cotterman, PhD
Exploring a Real-World Model of Sustainable and Circular Business in Pittsburgh
Peter Fadoul
Stephanie Potter


text:
Priced Out of the Citi
Draft News and More
« The Redskins Considering Terrelle Pryor?
2012 NFL Draft Rankings: Centers »
Inside The Numbers: Drafting A Super Bowl
The grind of playing 16 regular season games and in some cases, like the 2010 Super Bowl Champion Green Bay Packers, 20 games overall during one calendar season creates tremendous wear and tear on the bodies of pro football players. By season's end, after the grind, it's depth that puts a team over the top.
After another season of covering games it's spring cleaning time…time to decide what to toss from the endless amount of programs, paper, and team guides. When I came across the flip card for Super Bowl XLV between the Green Bay Packers and Pittsburgh Steelers (which shows the starters along with the entire roster), I decided to take another look at who started the game. One thing led to another and before I knew it, I was jotting down the draft round each starter was selected and we were going Inside the
This entry was posted on June 3, 2011 at 5:34 pm, and is filled under NFL Draft. You can skip to the end and leave a response. Pinging is currently not allowed.
Monday Live Podcast: Fantasy Football Insiders
PFWA 2012 Jack Horrigan Award | Tom Dimitroff
NFL Draft Countdown | No.23 DJ Fluker
NFL Draft Countdown | No.22 Keenan Allen
NFL Draft Countdown | No.21 Barkevious Mingo
New York Yankees Tickets are a member of Major League Baseball's American League East Division.
Since 1992, the Baltimore Orioles Tickets have played their home games at Oriole Park at Camden Yards.
Are you looking to attend some MLB games this season? Get your MLB Tickets this season: New York Mets Tickets, Minnesota Twins Tickets, Toronto Blue Jays Tickets or even Seattle's best, the Seattle Mariners Tickets. Enjoy a great evening in Detroit and get your Detroit Tigers Tickets today.
The best matchups for this season have to be the Phillies Red Sox Tickets, Phillies Pirates Tickets so get your Phillies Tickets today!
White Sox Tickets
The Chicago White Sox are a Major League Baseball team based in Chicago, Illinois. The White Sox play in the American League's Central Division.
Atlanta Braves Tickets
The Atlanta Braves are a professional baseball team based in Atlanta, Georgia. The Braves are a member of the Eastern Division of Major League Baseball's National League.
Texas Rangers Tickets
The Texas Rangers are a professional baseball team in Texas, representing the Dallas-Ft.Worth metropolitan area.
San Diego Padres Tickets
The San Diego Padres are a Major League Baseball team based in San Diego, California.
Visiting Florida you should try to get your hands on Tampa Bay Rays Tickets and Florida Marlins Tickets.
The San Francisco Giants Tickets are a Major League Baseball team based in San Francisco, California, who currently play in the National League West Division.
The Cleveland Indians Tickets are a professional baseball team based in Cleveland, Ohio. They are in the Central Division of Major League Baseball's American League. If you are looking to attend another Ohio baseball team check out the Cincinnati Reds Tickets.
Arizona Diamondbacks Tickets
From 1998 to the present, they have played in Chase Field. Also known as the D-backs and Snakes.
Seattle Mariners Tickets
The Seattle Mariners are an American professional baseball team based in Seattle, Washington.
Oakland Athletics Tickets
The Oakland Athletics are a Major League Baseball team based in Oakland, California.
Milwaukee Brewers Tickets
The Milwaukee Brewers are a professional baseball team based in Milwaukee, Wisconsin, currently playing in the Central Division of Major League Baseball's National League.
RSS Feeds Top


text:
Social Media, etc.
Imagining an Interconnected World
Meet Suzie
The UVA Story
Dear Snapchat, It didn't have to go down like this
Things are not good in Snapchat land. On Thursday, Snap announced that it had gained a mere 7M daily active users in the past 3 months- up from 166M in May. "So?", you might say, "that's still growth- at least they aren't loosing users". Well…. The problem is that during the previous 3 month period, they had gained 8M daily active users. Healthy social networks mean exponential user growth and that's what investors expect to see.
But that's really been the problem all along for Snapchat- it never invested in the user experience to become a social network. For me, that's really the unforgivable part of this whole saga. It could have been great. But they decided not to listen to the voices along the way who have been clamoring for features such as robust native analytics (still MIA), links (added July 2017), group chat (added December 2016) long before Facebook ever got into their turf. The lack of those key features kept many individuals and, importantly, brand strategists from adopting Snapchap as a core network. Instead we used it for the tech, not for the relationship outcomes. That's why Facebook was able to swoop in and undermine it so quickly with Instagram.
Successful social networks have technology and community. That's why Facebook was able to survive Google+ but Snapchat is becoming entirely undermined by Facebook.
Google+ went after Facebook HARD. People, including myself, loved the Google+ interface. Recall that this was around the time that Facebook was facing uproar from their original users about privacy, the algorithm and ad-creep. We were looking for an alternative to the social network whose founder said that privacy was no longer a social norm. The hope was that Google, with their motto at the time of "Don't be evil" could provide that. Moreover, Google+ had better tech as well- from higher resolution photos to native video integration with YouTube and Hangouts (light years before Facebook would introduce video)- they outpaced them. And yet they could not, for the life of them, build a user base.
Fast forward to the present where Snapchat is in the battle of its life because, wait for it, Facebook is copying features. Yes it's true that Snapchat can still hold onto the "cool" factor amount teens. But that same 2016 report spelled doom for monetization-showing that teens hate ads or, even worse, simply ignore them. Also let's be honest once those teens mature out of their anti-establishment phase, there is a very strong chance that they will migrate over to the platforms that their sorority sisters, frat brothers, universities, and yes (gasp) even parents are on (college kids get homesick).
In his article "Why I'm leaving Snapchat and so are all of your friends" Owen Williams summed it up well:
I think, after years of being an active Snapchat user and fan, I've decided to move on. The service was fun, but I've realized recently that it doesn't offer anything unique, and even if Facebook was copying the company in the first place, it's done a better job than Snapchat ever could.
The majority of my friends have moved across, and those who initially relented seem to have started getting their feet wet with Instagram too. Facebook, be it accidentally or on purpose, has created an Instagram renaissance that has us more addicted than ever before because we get to see beautiful photos in the feed, then the raw, real life stuff in stories.
Google+ was pretty much doomed from the start but Snapchat didn't have to be. They had several years of unsullied market share that Facebook tried and failed to land grab. But instead of thinking through ways to strategically strengthen their signal, build out platform stickiness and monetization opportunities, their boy kings decided that they were too good for such things. They were Snapchat.
Well. Good luck with that is all I can say.
Posted on August 11, 2017 August 11, 2017 Categories July 2017 Challenge, marketing, social mediaTags facebook, Google+, snapchat, social mediaLeave a comment on Dear Snapchat, It didn't have to go down like this
Our Shared Rainbow Moment
Only a few days in my life can compare to the feelings of pure joy and solidarity that I felt on June 26, 2015. It's one of those markers that I believe is going to go down in history as collective memory- a "what were you doing when you heard" moment in time that we share with each other in years to come. Events such as those make us want to come together as humans- introverts and extraverts alike- to be with each other. Sharing those moments with others is a major part of the experience and we see spontaneous congregation in city centers to be with each other. Everyone wants to be a part of it.
On June 26th we saw the way that Facebook and Twitter in particular have become virtual city centers. Within minutes of the announcement profile photos across the social space were redone in various shades of rainbow. Tons of Brands joined in with, in my opinion, no real visible marketing strategy, rather they seemed to be motivated feeling that they should be a part of this momentous occasion. (A H2H moment!) And while Facebook didn't change their logo they launched the "Celebrate Pride Tool" to create a rainbow filter of your profile picture. By the end of the day my Twitter and Facebook feeds were full of rainbows. A visual testament to our unity at a momentous time in history.
What does this mean? Probably not much in the long run. A bitter election campaign is right around the corner. Our profiles and feeds will be full of various opinions and divisions. Statistically unfollowing and de friending WILL occur. But let's remember this moment. The field of rainbows- each an individual decision to create. Each representing a moment when we made a very human decision- to stand up and be counted and unite our voices together in celebration of equality.
Yes I'm waxing eloquent and no I don't care. Blame it on the rainbow.
Posted on June 27, 2015 July 9, 2015 Categories community, engagement, facebook, social media, twitterTags #UVAdormsearch, community building, engagement, facebook, social media, twitter1 Comment on Our Shared Rainbow Moment
Here's Why Your Facebook Strategy Is Obsolete
Facebook's goal is to make it impossible for brands to not include paid posts in their content strategy. That's not breaking news. You would have had to be living under a rock not to notice this trend with their two algorithm updates last year. Many strategists, including myself, have written posts on how to update strategy to combat this. In general the advice has been to increase the number of posts as well as the confusing debate over whether it's better to post photo memes or links to photos. The overall message is to keep doing what you've been doing as long as you incorporate several additional tips and tricks.
According to a report released this week by Simply Measured, the top 10 Brands on Facebook are seeing their total monthly engagement fall by 40% since last year despite increasing their posts by 20.1% during the same period. This image taken from the SimplyMeasured report says it all:
credit: simplymeasured
Let's think about what this means.
Accordingly to SimplyMeasured, these top 10 brands have a collective audience of almost 358 million. The list includes some of the social media superstar brands:
MTV*
Harley-Davidson*
Audi USA
All of these brands have dedicated social media teams and spend millions of dollars in campaigns.
Moreover, all have received accolades at various times for their work in social media. In March of this year, Mercedes-Benz received the AM 2014 award for Best Social Media Campaign. Starbucks and Disney are consistently touted as cutting edge in social media adoption and Intel, as always, leads the way on employee advocacy via social. MTV and Harley-Davidson are outliers in these results as their level of engagement increased over the past year. Although when the report focused on per-post engagement MTV joined the rest of the brands with a significant drop (38.05%) For such social media juggernaut that's a huge number.
The fact that the results are so uniform (with the exception of Harley-Davidson) tells us that this is more than a blip. Something on the foundational level has shifted and we need to call every one of our assumptions about Facebook Strategy into question.
In many ways this shouldn't be too surprising. Facebook strategy has always been simpler to craft than Twitter and it's been around far longer than Pinterest, Instagram, and Vine. So a lot of the discussion out there centers on best practices such as when to post rather than an underlying discussion of the nature of the social network. The focus has been on content rather than interaction. The prettier the better. That's one reason 4 of the 10 brands are automotive companies.
But thanks to the algorithm changes no matter how pretty your content brand page reach for unprompted posts is now often under 3% according to a report last December by Ignite Social Media Agency.
It's time for a reboot. We need to start thinking about Facebook in terms of a social network as opposed to a broadcasting platform.
Facebook was built around individual interaction and as much as they punish brands for posting content, when an individual posts or shares a page's content the game changes. According to a Stanford Study published last year, on average 35% of your Facebook Friends see your posts. Of course debate immediately ensued over these results and I have no desire to open it here. Simply consider this- the number of people who see posts when they come from an individual is dramatically higher than when a Brand's Page posts it.
There are additional benefits to tapping into the social network side of Facebook. A recent report from Kentico found that "69% of the consumers surveyed say a company's educational information is more credible when discovered through a friend or family member". So by building relationships with customers and encouraging them to share content from your Facebook Page a brand not only gains exponential increase in reach, it also knows that that content has a higher degree of credibility.
Bottomline: The changes to Facebook's algorithm necessitates a fundamental reboot of Brand Page Strategy. As strategists we must move strategy away from using Pages to broadcast content and instead use them to build relationships with the audience, encouraging them to share and interact with the content.
Posted on June 12, 2014 January 25, 2015 Categories facebook, marketing, social mediaTags facebook, SimplyMeasured, social strategyLeave a comment on Here's Why Your Facebook Strategy Is Obsolete
How to Craft A Facebook Strategy that Works
Building a social strategy for Facebook is getting more and more difficult thanks to their "quality control" features. Ignite Social Media estimates that with the latest rollout of changes on average 3% of your fan base will see unprompted posts. That's it. To make it worse, there's little information as to how that 3% is decided. Of course you can pay to promote posts which is some companies are now saying is essential to do. I was at the Brand Innovators Social Summit this past February at which Addie Connor, Chief Innovation Officer at SocialCode gave a keynote. Her basic point was that there is no reason to have a Facebook Page if you aren't promoting your posts.
Really? I'm not so sure. A key with all social media platforms is to start with understanding how your target customer interacts with them. Where do they go? How do they get their information? What makes them "like" or "share" a post? We've become so obsessed with getting "likes" on Facebook Pages that we've forgotten the central mechanism behind Facebook use. An underlying assumption behind this doom and gloom view of Facebook Page posts is that people consume Facebook primarily through their home feed. It's true that Facebook is pushing this model as much as possible. But the fact of the matter is that Facebook is not Twitter. It's about community. For this reason no matter how much Zuckerberg & co. attempt to modify it, the reason we use Facebook will never be the same as the reason we use Twitter.
For Twitter it would be absolutely disastrous if only 3% of your followers saw your posts. This is because on Twitter our chief way of consuming posts is through our timelines as opposed to visiting an individual's Twitter page. We might do targeted listening via hashtags but as long as you know which hashtag to include to reach your targeted community that's easily included in a social strategy.
Contrast this with how you approach Facebook. In particular think of how you see Page posts. Page posts hardly ever pop up on my home feed. And to be honest when they do I tend to ignore them. My reason for consuming Facebook is to keep in touch with my friends and family. I want to see what they post and share. So when they share something from a Facebook Page THAT'S when I pay attention.
See the difference? This is a different metric at play. Now of course there is also a percentage used to calculate whether I'll see my friend's post share on my home feed assuming I'm not tagged or they don't share it to my wall. But that's something that we can work with as social strategists. We know how to harness influencers and advocates. We know how to organize communities and create content that gets shared.
1. Harness Power Users
Facebook revolves around the social network. Pew and others have identified the prevalence of "power users" on Facebook who make up the vast majority of content that gets shared. These are the people who you want to engage on your Facebook Page. You want them to share your content with their friends. You want your page to be the one that they check out for the latest on "x". A great way to get them to return to your page is to engage them in conversation. Did they make a humorous comment on a post? Respond in kind.
2. Mobilize internal Influencers
We do this on Twitter and LinkedIn but not Facebook. Who are the thought-leaders in your organization? Get them to interact with your Facebook Posts. You might even think of sending you an email to alert your thought-leaders to an interesting thread on the Page that they should enter into. Encourage them to share your content.
3. Engage with like-minded Pages
Every Page is looking for engagement. So set up a mutually beneficial relationship. If a Page has an audience that you would like to reach or that is similar to yours getting them to share a link to your Page is a great way to increase your content views. Facebook is a concrete social network. The power of sharing means that your post can very easily go viral and THAT is the way that Facebook Pages become useful.
Here's a practical example: Craft-beers have very loyal followings and some have done a great job building a social media presence but they are always looking to get converts. Pubs and bars have a community presence but are always looking to get more customers in, particularly on weekdays. They also tend to have a weaker social media presence. But by supporting each other through their Facebook Pages each can exponentially increase their fan base. Also, going a step further, there is a high probability that at the intersection between the Craft Beer Community and Pub Community you'll find some strong advocates.
Bottom-line?
Changes to the Facebook algorithm do not herald the end of brand presence on Facebook. Rather they signal the need (which has been long in the making) of moving away from a broadcast model to a social network model of social marketing. Pulling out of Facebook is the absolute LAST thing brands should do. Rather, they should renew their focus on their Facebook campaign via the creation of a targeted social strategy.
Posted on April 6, 2014 January 25, 2015 Categories facebook, marketingTags facebook, marketing, social media, social media strategist, social strategistLeave a comment on How to Craft A Facebook Strategy that Works
Why Facebook Is Losing Millennials
On December 27th, the Guardian proclaimed Facebook to be dead, citing a growing perception among teens that it's for their parents and therefore not a place they want to be. This came out just in time for the end of the year survey articles of 2013 and 2014 forecasts and therefore got huge play. More recently, TIME magazine published a study saying that between 2015 and 2017 Facebook will lose 80% of its users.
A lot of attention has been giving to this emergent trend that Facebook is "uncool". Indeed Mark Zuckerberg recently said that Facebook doesn't care about being cool anymore. Well, bully for him. The fact, however, is that it really was never about the cool factor for millennials. Rather it was all about keeping in touch with your offline community. It filled a need for us and that's why Zuckerberg actually created it.
At college your social experience is suddenly much different. Instead of a thousand students (max) in your year like you had a high school you have several thousand all with different majors and courses. And even if you go to a smaller college you will still have to deal with your social circle being flung to the far corners of the country. I was at one of the first Universities to be let into the network in fall 2004 and I remember impatiently waiting for other Universities to be let in so that I could connect to my friends back home. When community colleges were included that was super awesome. We could actually KIT in realtime!
When the network expanded to allow parents to join once again I was at the right time to embrace that. I live across the country from my family and it proved and still proves to be a great way to keep in touch. Of course it did lead to the creation of protected photo albums the last thing you want is your grandma seeing that outfit you wore last Friday night! But the point is that I matured into the point at which I wanted to get in touch with them. Facebook grew as 20-something grew. We have driven the growth of Facebook. For that reason it's us rather than teens that stockholders should be worried about. We spend the most money, and are the most socially savvy. BUT, there's been an ongoing trend according to Pew of us "taking a break" from Facebook and for us this has nothing to do with Facebook being "not cool". The first wave of anger over Facebook and user drop off occurred when they started screwing with their Terms of Service and Privacy Settings in 2009. That was the first time that I ever contemplated deleting my account. And I know I was not alone.
As millennials, we care about the brand as a whole. We care about transparency and honesty. We like companies that are lovable. Yet Zuckerberg's Facebook is increasingly the antithesis of millennial values. Facebook seems to now go out of its way to show that it really couldn't care less about its users. And thanks to that attitude, I can safely say that as a twenty-something, I feel no loyalty to Facebook. Rather it is the network that I'm stuck with and believe me as soon as I can get away I will. That should be a sentiment far more troubling than any "cool" problem because, as a recent study predicts, each departure has a cascading effect, weakening the network.
What do you think about Facebook and Millennials? Tweet me @suzimcc with your thoughts!
Posted on January 28, 2014 January 25, 2015 Categories facebookTags facebook, millennials, privacy, TOS, ZuckerbergLeave a comment on Why Facebook Is Losing Millennials
Why A Social Strategist Should Be Your Next Hire
"Social (insert job title here)" has been a huge fad whose time is up. Just like "Tech" was big in the 1990s, "Social" took over in the late 2000s. In the mid-1990s, if you could set up a website for an organization you were a Big Deal- a tech guru. But by the early 2000s as webpages became easier to create and software companies began to standardize their programs, the "Tech-" jobs began to dry up. Computer skills were assumed, and while setting up the wireless network still generates a fair amount of cursing by and large the IT person can handle it- no all knowing tech guru needed.
The same thing is happening with social media. There was a glut of positions incorporating the term "social". Companies created social media accounts seemingly along the logic that more is always better. "Facebook", "Twitter", and "Hootsuite" were included on resumes where Microsoft Word and Excel has previously been.
Now in 2013, the hype over social media jobs has died down. Indeed, within many quarters, managing social media channels is the duty of the intern. A large part of this is because no one was able to figure out the ROI (Return of Investment) for a Facebook Page or Twitter account. So they downsized to an infrequently updated account with posts consisting of "We are excited to announce the rollout of [insert product here]". And for most Twitter is just a mystery. Brands now have social channels because they know that are supposed to have them in the same way that you're supposed to have a website. The direction is "keep things updated" in the same way that the front page of a website is updated. Investment is spent instead in integrated marketing campaigns- things that have a specific ROI tied to marketing. This is not to knock these types of campaigns- indeed they work very well. My point is that integrated marketing is just the tip of the iceberg of the potential of social media. It's right to move away from the hyphenated job titles- the hype is over. But this does not mean that social strategy should stop.
A social strategist is empowered with the mandate to implement and execute strategy. A social strategist knows what social media can and can't do. They are simultaneously big picture and detail oriented.
Several Brands have chosen to take the leap and invest in social strategy. By doing so they have generated a powerful network of advocates and influencers and also made a name for themselves within the social media community as innovators in their own right.
Casestudy 1: General Electric
GE wanted to communicate that they do more than create light bulbs. So they embarked upon the "innovation" campaign and included social strategy as a key component. A social media strategist knows that the power of social media comes from tapping into the pre-existing social media communities in a way that is keeping with the reason that individuals interact with these communities.
GE utilizes Pinterest, Facebook, and Tumblr in their campaign but, and this is where the social strategy comes in, each has a specific type of content designed to maximize the outreach to the target communities in a way that will spark conversation. But all content revolves around the concept of "innovation". Coming away from these pages that's the word that you come away with. That's why it matters and that's the ROI. A social strategist will insist that you clearly define your goals and then create a strategy to match that.
Casestudy 2: Maesrk
Maersk decided that they wanted to be more than a shadowy shipping line. They're a fascinating case because they are purely B2B. The average person is never going to give them money. But they recognized that in the age of faceless corporations there was considerable utility in generating a positive brand image. Other brands will want to work with you. The wisdom of this was borne out when one of their ships accidentally killed a whale. Of course the outcry occurred on social media. Since they already had defined social channels and had built up a community, they were able to genuinely respond to the situation with concern. They even created a Pinterest board in memory of the whale.
Maersk specifically decided to "unmask" themselves and create a community. Every piece of content they create, every social channel that they use, works towards this goal.
If you do not have a clear strategy behind using your social channels you are missing a huge opportunity. As far as ROI is concerned, it's best to think of it in terms of measurable projects with set goals just as you would for an ad campaign. Demanding a per-tweet or per-post ROI while the campaign is on-going is just like demanding a per-billboard ROI.
Hire a good social strategist, clearly communicate your goal, give a set budget, and watch them go. Your brand is already present on social. Even if, like Maersk, you are far removed from the B2C scene, you are only one accident or blunder away from becoming a trending hashtag.
Posted on January 21, 2014 January 27, 2015 Categories engagement, facebook, marketing, social media, twitterTags engagement, facebook, social media, social media strategist, twitter2 Comments on Why A Social Strategist Should Be Your Next Hire
How, After Four Years, I Finally Learned to Love LinkedIn
"(Insert Name Here) would like to add you to her network on LinkedIn. Register for an account now to connect with her" Remember that message? The one that finally got you to jump on the LinkedIn bandwagon? Mine came on May 12, 2009 from my future husband of all people so I finally figured I'd see what all the fuss was about and join.
I must admit, I was fairly underwhelmed. I went around and "connected" with members of my graduating class and of course some token high school buddies. I added a basic resume to my profile and then left. I probably checked it less than 100 times between 2009 and 2013 which for a social media nut is fairly mind-boggling. I just didn't "get" it. What was the draw? I was connected to most of these people on Facebook, why would I want to be part of their "LinkedIn network"? Looking back I now realize exactly what was wrong- to use tribal marketing terminology- my "tribe" wasn't active on the network.
This is an issue that Google+ is now dealing with. The whole "if you build it they will come" thing is much more difficult in the era of Facebook dominance. Social media means forging online connections. On Facebook they tend to be the online manifestation of offline relationships. On Twitter it's an opportunity to forge online relationships around common interests that can then turn into offline relationships. Social platforms like Instagram and Pinterest provide additional dimensions to these connections. The point is they all fit and make sense to our natural human state as social creatures. I already wrote a post about the steep learning curve to Twitter adoption as opposed to Facebook. But this is nothing compared with the hurdles that Google+ faces.
But I digress- the topic of this post is about LinkedIn so let's get back to that.
As I said, the issue for me with LinkedIn is that my tribe wasn't present- and by present I don't mean that they weren't users. Rather, they were not updating or active. There were no opportunities to exchange viewpoints. A social network's value comes from building relationships through conversations so if there's no one to talk to or if the conversation unfolds over a matter of weeks due to inconsistent usage then there's very little utility in using it. (once again- Google+)
BUT, as I said- I've learned to love LinkedIn. Why? Well, I started a job as a Community Strategist and Social Media Today which plunked me into a new tribe- one that uses LinkedIn effectively and this has made all the difference. I was motivated enough to even change my profile pic. I've done status updates for the first time EVER! I've shared content and I've been building my network.
There's a clear lesson here and it's one that echoes my prior post on Twitter. The Social Network must match the user's needs. No matter how brilliant the social strategy, it will amount to squat if it is built outside of existing social networks and communities. We must be active where our target audience is active.
I talked about this in a recent #SMTnews tweetchat. The topic was Google+ vs Facebook. The general consensus was that Google+ seems like a really cool thing but needs widespread adoption before it will be a viable social alternative to Facebook.
Now don't get me wrong, LinkedIn isn't at all my favorite network. But it's become something that I find myself checking daily and I'm becoming an engaged user. So the lesson of this story? Social media users must feel that the social network offers something for them AND there must be some type of social draw back to the network day after day. Otherwise profiles may be created, groups may be joined, but the power of the network is actually rather empty.
Posted on July 19, 2013 January 21, 2014 Categories engagement, social media, twitterTags facebook, Google+, LinkedIn, SMTnews, social media, twitter1 Comment on How, After Four Years, I Finally Learned to Love LinkedIn
Proudly powered by WordPress Theme: Twenty Fifteen.


It's time to get happy.
On Radio 4, Twitter and even the Six O'clock news, the message from new charity Action on Happiness is the same – it's time to set cynicism aside, and start cultivating more happiness in our society.
Their website is awash with videos of psychologists telling us that following a few simple tips, such as doing something kind or noting three good things about your day, can have a marked effect on our cotentment levels as a society.
It's new – who doesn't love a new idea?
The coverage it has received is exactly what a project with these characteristics will inevitably get, and rightly so. If more charity projects stood out like Action for Happiness does, we might even get more coverage for the solutions we propose, instead of the terrible problems we want to solve.
← Head in Cloud 365?