You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ji...@apache.org on 2017/01/24 08:18:30 UTC

[15/50] [abbrv] incubator-weex git commit: + [ios] iOS init.

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
new file mode 100644
index 0000000..105eccc
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Events/WXComponent+Events.m
@@ -0,0 +1,621 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXComponent+Events.h"
+#import "WXComponent.h"
+#import "WXComponent_internal.h"
+#import "WXSDKInstance.h"
+#import "WXAssert.h"
+#import "WXUtility.h"
+#import "WXSDKManager.h"
+#import <objc/runtime.h>
+#import <UIKit/UIGestureRecognizerSubclass.h>
+
+#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+
+@interface UITouch (WXTouchGestureRecognizer)
+
+@property (nonatomic, strong) NSNumber *wx_identifier;
+
+@end
+
+@implementation UITouch (WXTouchGestureRecognizer)
+
+- (NSNumber *)wx_identifier
+{
+    return objc_getAssociatedObject(self, _cmd);
+}
+
+- (void)setWx_identifier:(NSNumber *)wx_identifier
+{
+    objc_setAssociatedObject(self, @selector(wx_identifier), wx_identifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
+
+@interface UIGestureRecognizer (WXGesture)
+
+@property (nonatomic, strong) NSNumber *wx_identifier;
+
+@end
+
+@implementation UIGestureRecognizer (WXGesture)
+
+- (NSNumber *)wx_identifier
+{
+    NSNumber *identifier = objc_getAssociatedObject(self, _cmd);
+    if (!identifier) {
+        static NSUInteger _gestureIdentifier;
+        identifier = @(_gestureIdentifier++);
+        self.wx_identifier = identifier;
+    }
+    
+    return identifier;
+}
+
+- (void)setWx_identifier:(NSNumber *)wx_identifier
+{
+    objc_setAssociatedObject(self, @selector(wx_identifier), wx_identifier, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
+}
+
+@end
+
+@interface WXTouchGestureRecognizer : UIGestureRecognizer
+
+@property (nonatomic, assign) BOOL listenTouchStart;
+@property (nonatomic, assign) BOOL listenTouchMove;
+@property (nonatomic, assign) BOOL listenTouchEnd;
+@property (nonatomic, assign) BOOL listenTouchCancel;
+
+- (instancetype)initWithComponent:(WXComponent *)component NS_DESIGNATED_INITIALIZER;
+
+@end
+
+@implementation WXComponent (Events)
+
+#pragma mark Public
+
+- (void)fireEvent:(NSString *)eventName params:(NSDictionary *)params
+{
+    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+    NSTimeInterval timeSp = [[NSDate date] timeIntervalSince1970] * 1000;
+    [dict setObject:@(timeSp) forKey:@"timestamp"];
+    if (params) {
+        [dict addEntriesFromDictionary:params];
+    }
+    
+    [[WXSDKManager bridgeMgr] fireEvent:self.weexInstance.instanceId ref:self.ref type:eventName params:dict];
+}
+
+- (void)addEvent:(NSString *)addEventName
+{
+    WXAssertMainThread();
+}
+
+- (void)removeEvent:(NSString *)removeEventName
+{
+    WXAssertMainThread();
+}
+
+#pragma mark Add & Remove Event
+
+
+#define WX_ADD_EVENT(eventName, addSelector) \
+if ([addEventName isEqualToString:@#eventName]) {\
+    [self addSelector];\
+}
+
+#define WX_REMOVE_EVENT(eventName, removeSelector) \
+if ([removeEventName isEqualToString:@#eventName]) {\
+    [self removeSelector];\
+}
+
+- (void)_initEvents:(NSArray *)events
+{
+    for (NSString *addEventName in events) {
+        [self _addEventOnMainThread:addEventName];
+    }
+}
+
+- (void)_addEventOnMainThread:(NSString *)addEventName
+{
+    WX_ADD_EVENT(appear, addAppearEvent)
+    WX_ADD_EVENT(disappear, addDisappearEvent)
+    
+    WX_ADD_EVENT(click, addClickEvent)
+    WX_ADD_EVENT(swipe, addSwipeEvent)
+    WX_ADD_EVENT(longpress, addLongPressEvent)
+    
+    WX_ADD_EVENT(panstart, addPanStartEvent)
+    WX_ADD_EVENT(panmove, addPanMoveEvent)
+    WX_ADD_EVENT(panend, addPanEndEvent)
+    
+    WX_ADD_EVENT(touchstart, addTouchStartEvent)
+    WX_ADD_EVENT(touchmove, addTouchMoveEvent)
+    WX_ADD_EVENT(touchend, addTouchEndEvent)
+    WX_ADD_EVENT(touchcancel, addTouchCancelEvent)
+    
+    [self addEvent:addEventName];
+}
+
+- (void)_removeEventOnMainThread:(NSString *)removeEventName
+{
+    WX_REMOVE_EVENT(appear, removeAppearEvent)
+    WX_REMOVE_EVENT(disappear, removeDisappearEvent)
+    
+    WX_REMOVE_EVENT(click, removeClickEvent)
+    WX_REMOVE_EVENT(swipe, removeSwipeEvent)
+    WX_REMOVE_EVENT(longpress, removeLongPressEvent)
+    
+    WX_REMOVE_EVENT(panstart, removePanStartEvent)
+    WX_REMOVE_EVENT(panmove, removePanMoveEvent)
+    WX_REMOVE_EVENT(panend, removePanEndEvent)
+    
+    WX_REMOVE_EVENT(touchstart, removeTouchStartEvent)
+    WX_REMOVE_EVENT(touchmove, removeTouchMoveEvent)
+    WX_REMOVE_EVENT(touchend, removeTouchEndEvent)
+    WX_REMOVE_EVENT(touchcancel, removeTouchCancelEvent)
+    
+    [self removeEvent:removeEventName];
+}
+
+- (void)_removeAllEvents
+{
+    [self removeClickEvent];
+    [self removeLongPressEvent];
+    [self removePanStartEvent];
+    [self removePanMoveEvent];
+    [self removePanEndEvent];
+    [self removeTouchStartEvent];
+    [self removeTouchMoveEvent];
+    [self removeTouchEndEvent];
+    [self removeTouchCancelEvent];
+    [self removeSwipeEvent];
+}
+
+#pragma mark - Appear & Disappear
+
+- (void)addAppearEvent
+{
+    _appearEvent = YES;
+    [self.ancestorScroller addScrollToListener:self];
+}
+
+- (void)addDisappearEvent
+{
+    _disappearEvent = YES;
+    [self.ancestorScroller addScrollToListener:self];
+}
+
+- (void)removeAppearEvent
+{
+    _appearEvent = NO;
+    [self.ancestorScroller removeScrollToListener:self];
+}
+
+- (void)removeDisappearEvent
+{
+    _disappearEvent = NO;
+    [self.ancestorScroller removeScrollToListener:self];
+}
+
+#pragma mark - Click Event
+
+- (void)addClickEvent
+{
+    if (!_tapGesture) {
+        _tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onClick:)];
+        _tapGesture.delegate = self;
+        [self.view addGestureRecognizer:_tapGesture];
+    }
+}
+
+- (void)removeClickEvent
+{
+    if (_tapGesture) {
+        [self.view removeGestureRecognizer:_tapGesture];
+        _tapGesture.delegate = nil;
+        _tapGesture = nil;
+    }
+}
+
+- (void)onClick:(__unused UITapGestureRecognizer *)recognizer
+{
+    [self fireEvent:@"click" params:nil];
+}
+
+#pragma mark - Swipe event
+
+- (void)addSwipeEvent
+{
+    if (_swipeGestures) {
+        return;
+    }
+    
+    _swipeGestures = [NSMutableArray arrayWithCapacity:4];
+    
+    // It's a little weird because the UISwipeGestureRecognizer.direction property is an options-style bit mask, but each recognizer can only handle one direction
+    SEL selector = @selector(onSwipe:);
+    UISwipeGestureRecognizer *upSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+                                                                                            action:selector];
+    upSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionUp;
+    upSwipeRecognizer.delegate = self;
+    [_swipeGestures addObject:upSwipeRecognizer];
+    [self.view addGestureRecognizer:upSwipeRecognizer];
+    
+    UISwipeGestureRecognizer *downSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+                                                                                              action:selector];
+    downSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionDown;
+    downSwipeRecognizer.delegate = self;
+    [_swipeGestures addObject:downSwipeRecognizer];
+    [self.view addGestureRecognizer:downSwipeRecognizer];
+    
+    UISwipeGestureRecognizer *rightSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+                                                                                               action:selector];
+    rightSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionRight;
+    rightSwipeRecognizer.delegate = self;
+    [_swipeGestures addObject:rightSwipeRecognizer];
+    [self.view addGestureRecognizer:rightSwipeRecognizer];
+    
+    UISwipeGestureRecognizer *leftSwipeRecognizer = [[UISwipeGestureRecognizer alloc] initWithTarget:self
+                                                                                              action:selector];
+    leftSwipeRecognizer.direction = UISwipeGestureRecognizerDirectionLeft;
+    leftSwipeRecognizer.delegate = self;
+    [_swipeGestures addObject:leftSwipeRecognizer];
+    [self.view addGestureRecognizer:leftSwipeRecognizer];
+}
+
+- (void)removeSwipeEvent
+{
+    if (_swipeGestures == nil) {
+        return;
+    }
+  
+    for (UISwipeGestureRecognizer *recognizer in _swipeGestures) {
+        recognizer.delegate = nil;
+        [self.view removeGestureRecognizer:recognizer];
+    }
+    
+    _swipeGestures = nil;
+}
+
+- (void)onSwipe:(UISwipeGestureRecognizer *)gesture
+{
+    UISwipeGestureRecognizerDirection direction = gesture.direction;
+    
+    NSString *directionString;
+    switch(direction) {
+        case UISwipeGestureRecognizerDirectionLeft:
+            directionString = @"left";
+            break;
+        case UISwipeGestureRecognizerDirectionRight:
+            directionString = @"right";
+            break;
+        case UISwipeGestureRecognizerDirectionUp:
+            directionString = @"up";
+            break;
+        case UISwipeGestureRecognizerDirectionDown:
+            directionString = @"down";
+            break;
+        default:
+            directionString = @"unknown";
+    }
+    
+    CGPoint screenLocation = [gesture locationInView:self.view.window];
+    CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
+    NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
+    [self fireEvent:@"swipe" params:@{@"direction":directionString, @"changedTouches":resultTouch ? @[resultTouch] : @[]}];
+}
+
+#pragma mark - Long Press
+
+- (void)addLongPressEvent
+{
+    if (!_longPressGesture) {
+        _longPressGesture = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(onLongPress:)];
+        _longPressGesture.delegate = self;
+        [self.view addGestureRecognizer:_longPressGesture];
+    }
+}
+
+- (void)removeLongPressEvent
+{
+    if (_longPressGesture) {
+        [self.view removeGestureRecognizer:_longPressGesture];
+        _longPressGesture.delegate = nil;
+        _longPressGesture = nil;
+    }
+}
+
+- (void)onLongPress:(UILongPressGestureRecognizer *)gesture
+{
+    if (gesture.state == UIGestureRecognizerStateBegan) {
+        CGPoint screenLocation = [gesture locationInView:self.view.window];
+        CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
+        NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
+        [self fireEvent:@"longpress" params:@{@"changedTouches":resultTouch ? @[resultTouch] : @[]}];
+    } else if (gesture.state == UIGestureRecognizerStateEnded) {
+        gesture.wx_identifier = nil;
+    }
+}
+
+#pragma makr - Pan
+
+- (void)addPanGesture
+{
+    if (!_panGesture) {
+        _panGesture = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(onPan:)];
+        _panGesture.delegate = self;
+        [self.view addGestureRecognizer:_panGesture];
+    }
+}
+
+- (void)addPanStartEvent
+{
+    _listenPanStart = YES;
+    [self addPanGesture];
+}
+
+- (void)addPanMoveEvent
+{
+    _listenPanMove = YES;
+    [self addPanGesture];
+}
+
+- (void)addPanEndEvent
+{
+    _listenPanEnd = YES;
+    [self addPanGesture];
+}
+
+- (void)onPan:(UIPanGestureRecognizer *)gesture
+{
+    CGPoint screenLocation = [gesture locationInView:self.view.window];
+    CGPoint pageLoacation = [gesture locationInView:self.weexInstance.rootView];
+    NSString *eventName;
+    NSDictionary *resultTouch = [self touchResultWithScreenLocation:screenLocation pageLocation:pageLoacation identifier:gesture.wx_identifier];
+    
+    if (gesture.state == UIGestureRecognizerStateBegan) {
+        eventName = @"panstart";
+    } else if (gesture.state == UIGestureRecognizerStateEnded) {
+        eventName = @"panend";
+        gesture.wx_identifier = nil;
+    } else if (gesture.state == UIGestureRecognizerStateChanged) {
+        eventName = @"panmove";
+    }
+    
+    if (eventName) {
+        [self fireEvent:eventName params:@{@"changedTouches":resultTouch ? @[resultTouch] : @[]}];
+    }
+}
+
+- (void)removePanStartEvent
+{
+    _listenPanStart = NO;
+    [self checkRemovePanGesture];
+}
+
+- (void)removePanMoveEvent
+{
+    _listenPanMove = NO;
+    [self checkRemovePanGesture];
+}
+
+- (void)removePanEndEvent
+{
+    _listenPanEnd = NO;
+    [self checkRemovePanGesture];
+}
+
+- (void)checkRemovePanGesture
+{
+    if (_panGesture && !_listenPanStart && !_listenPanMove && !_listenPanEnd) {
+        [self.view removeGestureRecognizer:_panGesture];
+        _panGesture.delegate = nil;
+        _panGesture = nil;
+    }
+}
+
+#pragma mark - Touch Event
+
+- (WXTouchGestureRecognizer *)touchGesture
+{
+    if (!_touchGesture) {
+        _touchGesture = [[WXTouchGestureRecognizer alloc] initWithComponent:self];
+        _touchGesture.delegate = self;
+        [self.view addGestureRecognizer:_touchGesture];
+    }
+    
+    return _touchGesture;
+}
+
+- (void)addTouchStartEvent
+{
+    self.touchGesture.listenTouchStart = YES;
+}
+
+- (void)addTouchMoveEvent
+{
+    self.touchGesture.listenTouchMove = YES;
+}
+
+- (void)addTouchEndEvent
+{
+    self.touchGesture.listenTouchEnd = YES;
+}
+
+- (void)addTouchCancelEvent
+{
+    self.touchGesture.listenTouchCancel = YES;
+}
+
+- (void)removeTouchStartEvent
+{
+    _touchGesture.listenTouchStart = NO;
+    [self checkRemoveTouchGesture];
+}
+
+- (void)removeTouchMoveEvent
+{
+    _touchGesture.listenTouchMove = NO;
+    [self checkRemoveTouchGesture];
+}
+
+- (void)removeTouchEndEvent
+{
+    _touchGesture.listenTouchEnd = NO;
+    [self checkRemoveTouchGesture];
+}
+
+- (void)removeTouchCancelEvent
+{
+    _touchGesture.listenTouchCancel = NO;
+    [self checkRemoveTouchGesture];
+}
+
+- (void)checkRemoveTouchGesture
+{
+    if (_touchGesture && !_touchGesture.listenTouchStart && !_touchGesture.listenTouchMove && !_touchGesture.listenTouchEnd && !_touchGesture.listenTouchCancel) {
+        [self.view removeGestureRecognizer:_touchGesture];
+        _touchGesture.delegate = nil;
+        _touchGesture = nil;
+    }
+}
+
+#pragma mark - UIGestureRecognizerDelegate
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
+{
+    return YES;
+}
+
+- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
+{
+    return YES;
+}
+
+- (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer
+{
+    // trigger touches
+    if ([gestureRecognizer isKindOfClass:[WXTouchGestureRecognizer class]]) {
+        return YES;
+    }
+
+//#ifdef DEBUG
+//    if ([gestureRecognizer isKindOfClass:[WXDebugLongPressGestureRecognizer class]]
+//        || [otherGestureRecognizer isKindOfClass:[WXDebugLongPressGestureRecognizer class]]) {
+//        return YES;
+//    }
+//#endif
+    
+    return NO;
+}
+
+#pragma mark - Utils
+
+- (NSDictionary *)touchResultWithScreenLocation:(CGPoint)screenLocation pageLocation:(CGPoint)pageLocation identifier:(NSNumber *)identifier
+{
+    NSMutableDictionary *resultTouch = [[NSMutableDictionary alloc] initWithCapacity:5];
+    resultTouch[@"screenX"] = @(screenLocation.x/WXScreenResizeRadio());
+    resultTouch[@"screenY"] = @(screenLocation.y/WXScreenResizeRadio());
+    resultTouch[@"pageX"] = @(pageLocation.x/WXScreenResizeRadio());
+    resultTouch[@"pageY"] = @(pageLocation.y/WXScreenResizeRadio());
+    resultTouch[@"identifier"] = identifier;
+    
+    return resultTouch;
+}
+
+@end
+
+@implementation WXTouchGestureRecognizer
+{
+    __weak WXComponent *_component;
+    NSUInteger _touchIdentifier;
+}
+
+- (instancetype)initWithTarget:(id)target action:(SEL)action
+{
+    return [self initWithComponent:nil];;
+}
+
+- (instancetype)initWithComponent:(WXComponent *)component
+{
+    if (self = [super initWithTarget:self action:@selector(touchResponse:)]) {
+        _component = component;
+        
+        _listenTouchStart = NO;
+        _listenTouchEnd = NO;
+        _listenTouchMove = NO;
+        _listenTouchCancel = NO;
+        
+        self.cancelsTouchesInView = NO;
+    }
+    
+    return self;
+}
+
+- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    [super touchesBegan:touches withEvent:event];
+    
+    if (_listenTouchStart) {
+        [self fireTouchEvent:@"touchstart" withTouches:touches];
+    }
+}
+
+- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    [super touchesMoved:touches withEvent:event];
+    
+    if (_listenTouchMove) {
+        [self fireTouchEvent:@"touchmove" withTouches:touches];
+    }
+}
+
+- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    
+    [super touchesEnded:touches withEvent:event];
+    
+    if (_listenTouchEnd) {
+        [self fireTouchEvent:@"touchend" withTouches:touches];
+    }
+}
+
+- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
+{
+    [super touchesCancelled:touches withEvent:event];
+    
+    if (_listenTouchCancel) {
+        [self fireTouchEvent:@"touchcancel" withTouches:touches];
+    }
+}
+
+- (void)fireTouchEvent:(NSString *)eventName withTouches:(NSSet<UITouch *> *)touches
+{
+    NSMutableArray *resultTouches = [NSMutableArray new];
+    
+    for (UITouch *touch in touches) {
+        CGPoint screenLocation = [touch locationInView:touch.window];
+        CGPoint pageLocation = [touch locationInView:_component.weexInstance.rootView];
+        if (!touch.wx_identifier) {
+            touch.wx_identifier = @(_touchIdentifier++);
+        }
+        NSDictionary *resultTouch = [_component touchResultWithScreenLocation:screenLocation pageLocation:pageLocation identifier:touch.wx_identifier];
+        [resultTouches addObject:resultTouch];
+    }
+    
+    [_component fireEvent:eventName params:@{@"changedTouches":resultTouches ?: @[]}];
+}
+
+- (void)touchResponse:(UIGestureRecognizer *)gesture
+{
+    
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h
new file mode 100644
index 0000000..8fb1370
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.h
@@ -0,0 +1,14 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import <Foundation/Foundation.h>
+#import "WXNavigationProtocol.h"
+
+@interface WXNavigationDefaultImpl : NSObject <WXNavigationProtocol>
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m
new file mode 100644
index 0000000..47d647d
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNavigationDefaultImpl.m
@@ -0,0 +1,353 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXNavigationDefaultImpl.h"
+#import "WXBaseViewController.h"
+#import "WXSDKManager.h"
+#import "WXComponent.h"
+#import "WXImgLoaderProtocol.h"
+#import "WXHandlerFactory.h"
+#import "WXConvert.h"
+
+@interface WXBarButton :UIButton
+
+@property (nonatomic, strong) NSString *instanceId;
+@property (nonatomic, strong) NSString *nodeRef;
+@property (nonatomic)  WXNavigationItemPosition position;
+
+@end
+
+@implementation WXBarButton
+
+@end
+
+@implementation WXNavigationDefaultImpl
+
+#pragma mark -
+#pragma mark WXNavigationProtocol
+
+- (id<WXImgLoaderProtocol>)imageLoader
+{
+    static id<WXImgLoaderProtocol> imageLoader;
+    static dispatch_once_t onceToken;
+    dispatch_once(&onceToken, ^{
+        imageLoader = [WXHandlerFactory handlerForProtocol:@protocol(WXImgLoaderProtocol)];
+    });
+    return imageLoader;
+}
+
+- (id)navigationControllerOfContainer:(UIViewController *)container{
+    return container.navigationController;
+}
+
+- (void)pushViewControllerWithParam:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+                      withContainer:(UIViewController *)container
+{
+    if (0 == [param count] || !param[@"url"] || !container) {
+        [self callback:block code:MSG_PARAM_ERR data:nil];
+        return;
+    }
+    
+    BOOL animated = YES;
+    NSString *obj = [[param objectForKey:@"animated"] uppercaseString];
+    if (obj && [obj isEqualToString:@"false"]) {
+        animated = NO;
+    }
+    
+    WXBaseViewController *vc = [[WXBaseViewController alloc]initWithSourceURL:[NSURL URLWithString:param[@"url"]]];
+    vc.hidesBottomBarWhenPushed = YES;
+    [container.navigationController pushViewController:vc animated:animated];
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)popViewControllerWithParam:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+                     withContainer:(UIViewController *)container
+{
+    BOOL animated = YES;
+    NSString *obj = [[param objectForKey:@"animated"] uppercaseString];
+    if (obj && [obj isEqualToString:@"false"]) {
+        animated = NO;
+    }
+    
+    [container.navigationController popViewControllerAnimated:animated];
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)popToRootViewControllerWithParam:(NSDictionary *)param
+                              completion:(WXNavigationResultBlock)block
+                           withContainer:(UIViewController *)container
+{
+    BOOL animated = YES;
+    NSString *obj = [[param objectForKey:@"animated"] uppercaseString];
+    if (obj && [obj isEqualToString:@"false"]) {
+        animated = NO;
+    }
+    
+    [container.navigationController popToRootViewControllerAnimated:animated];
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)setNavigationBarHidden:(BOOL)hidden animated:(BOOL)animated
+                 withContainer:(UIViewController *)container
+{
+    //If weex launchs in the WXBaseViewController, the navigationBar is hidden according to our design.
+    if (![container isKindOfClass:[WXBaseViewController class]]) {
+        return;
+    }
+    
+    container.navigationController.navigationBarHidden = hidden;
+}
+
+- (void)setNavigationBackgroundColor:(UIColor *)backgroundColor
+                       withContainer:(UIViewController *)container
+{
+    if (backgroundColor) {
+        container.navigationController.navigationBar.barTintColor = backgroundColor;
+    }
+}
+
+- (void)setNavigationItemWithParam:(NSDictionary *)param
+                          position:(WXNavigationItemPosition)position
+                        completion:(WXNavigationResultBlock)block
+                     withContainer:(UIViewController *)container
+{
+    switch (position) {
+        case WXNavigationItemPositionLeft:
+            [self setNaviBarLeftItem:param completion:block withContainer:container];
+            break;
+        case WXNavigationItemPositionRight:
+            [self setNaviBarRightItem:param completion:block withContainer:container];
+            break;
+        case WXNavigationItemPositionMore:
+            break;
+        case WXNavigationItemPositionCenter:
+            [self setNaviBarTitle:param completion:block withContainer:container];
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)clearNavigationItemWithParam:(NSDictionary *)param
+                            position:(WXNavigationItemPosition)position
+                          completion:(WXNavigationResultBlock)block
+                       withContainer:(UIViewController *)container
+{
+    switch (position) {
+        case WXNavigationItemPositionLeft:
+            [self clearNaviBarLeftItem:param completion:block withContainer:container];
+            break;
+        case WXNavigationItemPositionRight:
+            [self clearNaviBarRightItem:param completion:block withContainer:container];
+            break;
+        case WXNavigationItemPositionMore:
+            break;
+        case WXNavigationItemPositionCenter:
+            [self clearNaviBarTitle:param completion:block withContainer:container];
+            break;
+        default:
+            break;
+    }
+}
+
+- (void)setNaviBarLeftItem:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+              withContainer:(UIViewController *)container
+{
+    if (0 == [param count]) {
+        [self callback:block code:MSG_PARAM_ERR data:nil];
+        return;
+    }
+    
+    UIView *view = [self barButton:param position:WXNavigationItemPositionLeft withContainer:container];
+    
+    if (!view) {
+        [self callback:block code:MSG_FAILED data:nil];
+        return;
+    }
+    
+    container.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
+    
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)clearNaviBarLeftItem:(NSDictionary *) param completion:(WXNavigationResultBlock)block
+                withContainer:(UIViewController *)container
+{
+    container.navigationItem.leftBarButtonItem = nil;
+    
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)setNaviBarRightItem:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+              withContainer:(UIViewController *)container
+{
+    if (0 == [param count]) {
+        [self callback:block code:MSG_PARAM_ERR data:nil];
+        return;
+    }
+    
+    UIView *view = [self barButton:param position:WXNavigationItemPositionRight withContainer:container];
+    
+    if (!view) {
+        [self callback:block code:MSG_FAILED data:nil];
+        return;
+    }
+    
+    container.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:view];
+    
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)clearNaviBarRightItem:(NSDictionary *) param completion:(WXNavigationResultBlock)block
+                withContainer:(UIViewController *)container
+{
+    container.navigationItem.rightBarButtonItem = nil;
+    
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (void)setNaviBarTitle:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+          withContainer:(UIViewController *)container
+{
+    if (0 == [param count]) {
+        [self callback:block code:MSG_PARAM_ERR data:nil];
+        return;
+    }
+    
+    container.navigationItem.title = param[@"title"];
+    
+    [self callback:block code:MSG_SUCCESS data:nil];;
+}
+
+- (void)clearNaviBarTitle:(NSDictionary *)param completion:(WXNavigationResultBlock)block
+            withContainer:(UIViewController *)container
+{
+    container.navigationItem.title = @"";
+    
+    [self callback:block code:MSG_SUCCESS data:nil];
+}
+
+- (UIView *)barButton:(NSDictionary *)param position:(WXNavigationItemPosition)position
+        withContainer:(UIViewController *)container
+{
+    if (param[@"title"]) {
+        NSString *title = param[@"title"];
+
+        NSDictionary *attribute = @{NSFontAttributeName: [UIFont systemFontOfSize:18]};
+        CGSize size = [title boundingRectWithSize:CGSizeMake(70.0f, 18.0f) options: NSStringDrawingTruncatesLastVisibleLine | NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:attribute context:nil].size;
+        
+        WXBarButton *button = [WXBarButton buttonWithType:UIButtonTypeRoundedRect];
+        button.frame = CGRectMake(0, 0, size.width, size.height);
+        button.titleLabel.font = [UIFont systemFontOfSize:16];
+        [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
+        [button setTitleColor:[UIColor whiteColor]  forState:UIControlStateHighlighted];
+        [button setTitle:title forState:UIControlStateNormal];
+        [button setTitle:title forState:UIControlStateHighlighted];
+        [button addTarget:self action:@selector(onClickBarButton:) forControlEvents:UIControlEventTouchUpInside];
+        
+        button.instanceId = param[@"instanceId"];
+        button.nodeRef = param[@"nodeRef"];
+        button.position = position;
+        
+        NSString *color = param[@"titleColor"];
+        if (color) {
+            [button setTitleColor:[WXConvert UIColor:color] forState:UIControlStateNormal];
+            [button setTitleColor:[WXConvert UIColor:color] forState:UIControlStateHighlighted];
+        }
+        
+        return button;
+    }
+    else if (param[@"icon"]) {
+        NSString *icon = param[@"icon"];
+        
+        if (icon) {
+            WXBarButton *button = [WXBarButton buttonWithType:UIButtonTypeRoundedRect];
+            button.frame = CGRectMake(0, 0, 25, 25);
+            button.instanceId = param[@"instanceId"];
+            button.nodeRef = param[@"nodeRef"];
+            button.position = position;
+            [button addTarget:self action:@selector(onClickBarButton:) forControlEvents:UIControlEventTouchUpInside];
+            
+            [[self imageLoader] downloadImageWithURL:icon imageFrame:CGRectMake(0, 0, 25, 25) userInfo:nil completed:^(UIImage *image, NSError *error, BOOL finished) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    [button setBackgroundImage:image forState:UIControlStateNormal];
+                    [button setBackgroundImage:image forState:UIControlStateHighlighted];
+                });
+            }];
+            
+            return button;
+        }
+    }
+    
+    return nil;
+}
+
+- (UIView *)titleView:(NSDictionary *)param
+{
+    UIView *titleView = nil;
+    
+    if (param[@"title"]) {
+        UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 20.0f)];
+        label.backgroundColor = [UIColor clearColor];
+        label.textAlignment = NSTextAlignmentCenter;
+        label.lineBreakMode = NSLineBreakByTruncatingTail;
+        label.textColor = [UIColor whiteColor];
+        label.font = [UIFont systemFontOfSize:18.0f];
+        label.text = param[@"title"];
+        
+        UIColor *titleColor = param[@"titleColor"];
+        if (titleColor) {
+            label.textColor = [WXConvert UIColor:titleColor];
+        }
+        else {
+            label.textColor = [UIColor blueColor];
+        }
+        titleView = label;
+    }
+    
+    return titleView;
+}
+
+- (void)onClickBarButton:(id)sender
+{
+    WXBarButton *button = (WXBarButton *)sender;
+    if (button.instanceId) {
+        if (button.nodeRef)
+        {
+            [[WXSDKManager bridgeMgr] fireEvent:button.instanceId ref:button.nodeRef type:@"click" params:nil];
+        }
+        else
+        {
+            NSString *eventType;
+            switch (button.position) {
+                case WXNavigationItemPositionLeft:
+                    eventType = @"clickleftitem";
+                    break;
+                case WXNavigationItemPositionRight:
+                    eventType = @"clickrightitem";
+                    break;
+                case WXNavigationItemPositionMore:
+                    eventType = @"clickmoreitem";
+                    break;
+                default:
+                    break;
+            }
+            
+           [[WXSDKManager bridgeMgr] fireEvent:button.instanceId ref:WX_SDK_ROOT_REF type:eventType params:nil];
+        }
+    }
+}
+
+- (void)callback:(WXNavigationResultBlock)block code:(NSString *)code data:(NSDictionary *)reposonData
+{
+    if (block) {
+        block(code, reposonData);
+    }
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h
new file mode 100644
index 0000000..3578dd5
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.h
@@ -0,0 +1,14 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import <Foundation/Foundation.h>
+#import "WXNetworkProtocol.h"
+
+@interface WXNetworkDefaultImpl : NSObject <WXNetworkProtocol, NSURLSessionDelegate>
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m
new file mode 100644
index 0000000..72a0ed3
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Handler/WXNetworkDefaultImpl.m
@@ -0,0 +1,105 @@
+/**
+ * Created by Weex.
+ * Copyright (c) 2016, Alibaba, Inc. All rights reserved.
+ *
+ * This source code is licensed under the Apache Licence 2.0.
+ * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree.
+ */
+
+#import "WXNetworkDefaultImpl.h"
+
+@interface WXNetworkCallbackInfo : NSObject
+
+@property (nonatomic, copy) void(^sendDataCallback)(int64_t, int64_t);
+@property (nonatomic, copy) void(^responseCallback)(NSURLResponse *);
+@property (nonatomic, copy) void(^receiveDataCallback)(NSData *);
+@property (nonatomic, strong) NSMutableData *data;
+@property (nonatomic, copy) void(^compeletionCallback)(NSData *, NSError *);
+
+@end
+
+@implementation WXNetworkCallbackInfo
+
+@end
+
+@implementation WXNetworkDefaultImpl
+{
+    NSMutableDictionary *_callbacks;
+    NSURLSession *_session;
+}
+
+- (id)sendRequest:(NSURLRequest *)request withSendingData:(void (^)(int64_t, int64_t))sendDataCallback
+                                             withResponse:(void (^)(NSURLResponse *))responseCallback
+                                          withReceiveData:(void (^)(NSData *))receiveDataCallback
+                                          withCompeletion:(void (^)(NSData *, NSError *))compeletionCallback
+{
+    WXNetworkCallbackInfo *info = [WXNetworkCallbackInfo new];
+    info.sendDataCallback = sendDataCallback;
+    info.responseCallback = responseCallback;
+    info.receiveDataCallback = receiveDataCallback;
+    info.compeletionCallback = compeletionCallback;
+    
+    if (!_session) {
+        _session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
+                                                 delegate:self
+                                            delegateQueue:[NSOperationQueue mainQueue]];
+    }
+    
+    NSURLSessionDataTask *task = [_session dataTaskWithRequest:request];
+    if (!_callbacks) {
+        _callbacks = [NSMutableDictionary dictionary];
+    }
+    [_callbacks setObject:info forKey:task];
+    [task resume];
+    
+    return task;
+}
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
+                                didSendBodyData:(int64_t)bytesSent
+                                 totalBytesSent:(int64_t)totalBytesSent
+                       totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
+{
+    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+    if (info.sendDataCallback) {
+        info.sendDataCallback(totalBytesSent, totalBytesExpectedToSend);
+    }
+}
+
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task
+                                 didReceiveResponse:(NSURLResponse *)response
+                                  completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler
+{
+    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+    if (info.responseCallback) {
+        info.responseCallback(response);
+    }
+    completionHandler(NSURLSessionResponseAllow);
+}
+
+- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)task didReceiveData:(NSData *)data
+{
+    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+    if (info.receiveDataCallback) {
+        info.receiveDataCallback(data);
+    }
+    
+    NSMutableData *mutableData = info.data;
+    if (!mutableData) {
+        mutableData = [NSMutableData new];
+        info.data = mutableData;
+    }
+    
+    [mutableData appendData:data];
+}
+
+- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
+{
+    WXNetworkCallbackInfo *info = [_callbacks objectForKey:task];
+    if (info.compeletionCallback) {
+        info.compeletionCallback(info.data, error);
+    }
+    [_callbacks removeObjectForKey:task];
+}
+
+@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/185fe55c/ios/sdk/WeexSDK/Sources/Layout/Layout.c
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/Layout.c b/ios/sdk/WeexSDK/Sources/Layout/Layout.c
new file mode 100644
index 0000000..44d4d60
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Layout/Layout.c
@@ -0,0 +1,1324 @@
+
+/**
+ * Copyright (c) 2014, Facebook, Inc.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree. An additional grant
+ * of patent rights can be found in the PATENTS file in the same directory.
+ */
+
+#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_parent_max_height = -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 isStyleDimDefined(css_node_t *node, css_flex_direction_t axis) {
+  float value = node->style.dimensions[dim[axis]];
+  return !isUndefined(value) && value >= 0.0;
+}
+
+static bool isLayoutDimDefined(css_node_t *node, css_flex_direction_t axis) {
+  float value = node->layout.dimensions[dim[axis]];
+  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 (isLayoutDimDefined(node, axis)) {
+    return;
+  }
+  // We only run if there's a width or height defined
+  if (!isStyleDimDefined(node, axis)) {
+    return;
+  }
+
+  // The dimensions can never be smaller than the padding and border
+  node->layout.dimensions[dim[axis]] = fmaxf(
+    boundAxis(node, axis, node->style.dimensions[dim[axis]]),
+    getPaddingAndBorderAxis(node, axis)
+  );
+}
+
+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, float parentMaxHeight, 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);
+  float paddingAndBorderAxisColumn = getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
+
+  if (isMeasureDefined(node)) {
+    bool isResolvedRowDimDefined = isLayoutDimDefined(node, resolvedRowAxis);
+
+    float width = CSS_UNDEFINED;
+    css_measure_mode_t widthMode = CSS_MEASURE_MODE_UNDEFINED;
+    if (isStyleDimDefined(node, resolvedRowAxis)) {
+      width = node->style.dimensions[CSS_WIDTH];
+      widthMode = CSS_MEASURE_MODE_EXACTLY;
+    } else if (isResolvedRowDimDefined) {
+      width = node->layout.dimensions[dim[resolvedRowAxis]];
+      widthMode = CSS_MEASURE_MODE_EXACTLY;
+    } else {
+      width = parentMaxWidth -
+        getMarginAxis(node, resolvedRowAxis);
+      widthMode = CSS_MEASURE_MODE_AT_MOST;
+    }
+    width -= paddingAndBorderAxisResolvedRow;
+    if (isUndefined(width)) {
+      widthMode = CSS_MEASURE_MODE_UNDEFINED;
+    }
+
+    float height = CSS_UNDEFINED;
+    css_measure_mode_t heightMode = CSS_MEASURE_MODE_UNDEFINED;
+    if (isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+      height = node->style.dimensions[CSS_HEIGHT];
+      heightMode = CSS_MEASURE_MODE_EXACTLY;
+    } else if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+      height = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]];
+      heightMode = CSS_MEASURE_MODE_EXACTLY;
+    } else {
+      height = parentMaxHeight -
+        getMarginAxis(node, resolvedRowAxis);
+      heightMode = CSS_MEASURE_MODE_AT_MOST;
+    }
+    height -= getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
+    if (isUndefined(height)) {
+      heightMode = CSS_MEASURE_MODE_UNDEFINED;
+    }
+
+    // We only need to give a dimension for the text if we haven't got any
+    // for it computed yet. It can either be from the style attribute or because
+    // the element is flexible.
+    bool isRowUndefined = !isStyleDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;
+    bool isColumnUndefined = !isStyleDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&
+      isUndefined(node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]]);
+
+    // Let's not measure the text if we already know both dimensions
+    if (isRowUndefined || isColumnUndefined) {
+      css_dim_t measureDim = node->measure(
+        node->context,
+        
+        width,
+        widthMode,
+        height,
+        heightMode
+      );
+      if (isRowUndefined) {
+        node->layout.dimensions[CSS_WIDTH] = measureDim.dimensions[CSS_WIDTH] +
+          paddingAndBorderAxisResolvedRow;
+      }
+      if (isColumnUndefined) {
+        node->layout.dimensions[CSS_HEIGHT] = measureDim.dimensions[CSS_HEIGHT] +
+          paddingAndBorderAxisColumn;
+      }
+    }
+    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 = isLayoutDimDefined(node, mainAxis);
+  bool isCrossDimDefined = isLayoutDimDefined(node, crossAxis);
+  bool isMainRowDirection = isRowDirection(mainAxis);
+
+  int i;
+  int ii;
+  css_node_t* child;
+  css_flex_direction_t axis;
+
+  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 = CSS_UNDEFINED;
+    float maxHeight = CSS_UNDEFINED;
+    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 &&
+          !isStyleDimDefined(child, crossAxis)) {
+        child->layout.dimensions[dim[crossAxis]] = fmaxf(
+          boundAxis(child, crossAxis, node->layout.dimensions[dim[crossAxis]] -
+            paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
+          // You never want to go smaller than padding
+          getPaddingAndBorderAxis(child, crossAxis)
+        );
+      } else if (child->style.position_type == CSS_POSITION_ABSOLUTE) {
+        // Store a private linked list of absolutely positioned children
+        // so that we can efficiently traverse them later.
+        if (firstAbsoluteChild == NULL) {
+          firstAbsoluteChild = child;
+        }
+        if (currentAbsoluteChild != NULL) {
+          currentAbsoluteChild->next_absolute_child = child;
+        }
+        currentAbsoluteChild = child;
+
+        // Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
+        // left and right or top and bottom).
+        for (ii = 0; ii < 2; ii++) {
+          axis = (ii != 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
+          if (isLayoutDimDefined(node, axis) &&
+              !isStyleDimDefined(child, axis) &&
+              isPosDefined(child, leading[axis]) &&
+              isPosDefined(child, trailing[axis])) {
+            child->layout.dimensions[dim[axis]] = fmaxf(
+              boundAxis(child, axis, node->layout.dimensions[dim[axis]] -
+                getPaddingAndBorderAxis(node, axis) -
+                getMarginAxis(child, axis) -
+                getPosition(child, leading[axis]) -
+                getPosition(child, trailing[axis])),
+              // You never want to go smaller than padding
+              getPaddingAndBorderAxis(child, axis)
+            );
+          }
+        }
+      }
+
+      float nextContentDim = 0;
+
+      // It only makes sense to consider a child flexible if we have a computed
+      // dimension for the node->
+      if (isMainDimDefined && isFlex(child)) {
+        flexibleChildrenCount++;
+        totalFlexible += child->style.flex;
+
+        // Store a private linked list of flexible children so that we can
+        // efficiently traverse them later.
+        if (firstFlexChild == NULL) {
+          firstFlexChild = child;
+        }
+        if (currentFlexChild != NULL) {
+          currentFlexChild->next_flex_child = child;
+        }
+        currentFlexChild = child;
+
+        // Even if we don't know its exact size yet, we already know the padding,
+        // border and margin. We'll use this partial information, which represents
+        // the smallest possible size for the child, to compute the remaining
+        // available space.
+        nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +
+          getMarginAxis(child, mainAxis);
+
+      } else {
+        maxWidth = CSS_UNDEFINED;
+        maxHeight = CSS_UNDEFINED;
+
+        if (!isMainRowDirection) {
+          if (isLayoutDimDefined(node, resolvedRowAxis)) {
+            maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
+              paddingAndBorderAxisResolvedRow;
+          } else {
+            maxWidth = parentMaxWidth -
+              getMarginAxis(node, resolvedRowAxis) -
+              paddingAndBorderAxisResolvedRow;
+          }
+        } else {
+          if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+            maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
+                paddingAndBorderAxisColumn;
+          } else {
+            maxHeight = parentMaxHeight -
+              getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -
+              paddingAndBorderAxisColumn;
+          }
+        }
+
+        // This is the main recursive call. We layout non flexible children.
+        if (alreadyComputedNextLayout == 0) {
+          layoutNode(child, maxWidth, maxHeight, direction);
+        }
+
+        // Absolute positioned elements do not take part of the layout, so we
+        // don't use them to compute mainContentDim
+        if (child->style.position_type == CSS_POSITION_RELATIVE) {
+          nonFlexibleChildrenCount++;
+          // At this point we know the final size and margin of the element.
+          nextContentDim = getDimWithMargin(child, mainAxis);
+        }
+      }
+
+      // The element we are about to add would make us go to the next line
+      if (isNodeFlexWrap &&
+          isMainDimDefined &&
+          mainContentDim + nextContentDim > definedMainDim &&
+          // If there's only one element, then it's bigger than the content
+          // and needs its own line
+          i != startLine) {
+        nonFlexibleChildrenCount--;
+        alreadyComputedNextLayout = 1;
+        break;
+      }
+
+      // Disable simple stacking in the main axis for the current line as
+      // we found a non-trivial child-> The remaining children will be laid out
+      // in <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) ||
+              (alignItem == CSS_ALIGN_STRETCH && !isCrossDimDefined))) {
+        isSimpleStackCross = false;
+        firstComplexCross = i;
+      }
+
+      if (isSimpleStackMain) {
+        child->layout.position[pos[mainAxis]] += mainDim;
+        if (isMainDimDefined) {
+          setTrailingPosition(node, child, mainAxis);
+        }
+
+        mainDim += getDimWithMargin(child, mainAxis);
+        crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
+      }
+
+      if (isSimpleStackCross) {
+        child->layout.position[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
+        if (isCrossDimDefined) {
+          setTrailingPosition(node, child, crossAxis);
+        }
+      }
+
+      alreadyComputedNextLayout = 0;
+      mainContentDim += nextContentDim;
+      endLine = i + 1;
+    }
+
+    // <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 (isLayoutDimDefined(node, resolvedRowAxis)) {
+          maxWidth = node->layout.dimensions[dim[resolvedRowAxis]] -
+            paddingAndBorderAxisResolvedRow;
+        } else if (!isMainRowDirection) {
+          maxWidth = parentMaxWidth -
+            getMarginAxis(node, resolvedRowAxis) -
+            paddingAndBorderAxisResolvedRow;
+        }
+        maxHeight = CSS_UNDEFINED;
+        if (isLayoutDimDefined(node, CSS_FLEX_DIRECTION_COLUMN)) {
+          maxHeight = node->layout.dimensions[dim[CSS_FLEX_DIRECTION_COLUMN]] -
+            paddingAndBorderAxisColumn;
+        } else if (isMainRowDirection) {
+          maxHeight = parentMaxHeight -
+            getMarginAxis(node, CSS_FLEX_DIRECTION_COLUMN) -
+            paddingAndBorderAxisColumn;
+        }
+
+        // And we recursively call the layout algorithm for this child
+        layoutNode(currentFlexChild, maxWidth, maxHeight, direction);
+
+        child = currentFlexChild;
+        currentFlexChild = currentFlexChild->next_flex_child;
+        child->next_flex_child = NULL;
+      }
+
+    // We use justifyContent to figure out how to allocate the remaining
+    // space available
+    } else if (justifyContent != CSS_JUSTIFY_FLEX_START) {
+      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) {
+          /*eslint-disable */
+          // This variable is intentionally re-defined as the code is transpiled to a block scope language
+          css_align_t alignItem = getAlignItem(node, child);
+          /*eslint-enable */
+          if (alignItem == CSS_ALIGN_STRETCH) {
+            // You can only stretch if the dimension has not already been defined
+            // previously.
+            if (!isStyleDimDefined(child, crossAxis)) {
+              float dimCrossAxis = child->layout.dimensions[dim[crossAxis]];
+              child->layout.dimensions[dim[crossAxis]] = fmaxf(
+                boundAxis(child, crossAxis, containerCrossAxis -
+                  paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
+                // You never want to go smaller than padding
+                getPaddingAndBorderAxis(child, crossAxis)
+              );
+
+              // If the size has changed, and this child has children we need to re-layout this child
+              if (dimCrossAxis != child->layout.dimensions[dim[crossAxis]] && child->children_count > 0) {
+                // Reset child margins before re-layout as they are added back in layoutNode and would be doubled
+                child->layout.position[leading[mainAxis]] -= getLeadingMargin(child, mainAxis) +
+                  getRelativePosition(child, mainAxis);
+                child->layout.position[trailing[mainAxis]] -= getTrailingMargin(child, mainAxis) +
+                  getRelativePosition(child, mainAxis);
+                child->layout.position[leading[crossAxis]] -= getLeadingMargin(child, crossAxis) +
+                  getRelativePosition(child, crossAxis);
+                child->layout.position[trailing[crossAxis]] -= getTrailingMargin(child, crossAxis) +
+                  getRelativePosition(child, crossAxis);
+
+                layoutNode(child, maxWidth, maxHeight, direction);
+              }
+            }
+          } 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 (isLayoutDimDefined(child, 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 (isLayoutDimDefined(node, axis) &&
+          !isStyleDimDefined(currentAbsoluteChild, axis) &&
+          isPosDefined(currentAbsoluteChild, leading[axis]) &&
+          isPosDefined(currentAbsoluteChild, trailing[axis])) {
+        currentAbsoluteChild->layout.dimensions[dim[axis]] = fmaxf(
+          boundAxis(currentAbsoluteChild, axis, node->layout.dimensions[dim[axis]] -
+            getBorderAxis(node, axis) -
+            getMarginAxis(currentAbsoluteChild, axis) -
+            getPosition(currentAbsoluteChild, leading[axis]) -
+            getPosition(currentAbsoluteChild, trailing[axis])
+          ),
+          // You never want to go smaller than padding
+          getPaddingAndBorderAxis(currentAbsoluteChild, axis)
+        );
+      }
+
+      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, float parentMaxHeight, 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_parent_max_height, parentMaxHeight) &&
+    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_parent_max_height = parentMaxHeight;
+    layout->last_direction = direction;
+
+    for (int i = 0, childCount = node->children_count; i < childCount; i++) {
+      resetNodeLayout(node->get_child(node->context, i));
+    }
+
+    layoutNodeImpl(node, parentMaxWidth, parentMaxHeight, 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];
+  }
+}
+
+void resetNodeLayout(css_node_t *node) {
+  node->layout.dimensions[CSS_WIDTH] = CSS_UNDEFINED;
+  node->layout.dimensions[CSS_HEIGHT] = CSS_UNDEFINED;
+  node->layout.position[CSS_LEFT] = 0;
+  node->layout.position[CSS_TOP] = 0;
+}