dist/famous-flex.js
function ListLayout(context, options) {

        // Local data
        var size = context.size;
        var direction = context.direction;
        var alignment = context.alignment;
        var revDirection = direction ? 0 : 1;
        var offset;
        var margins = LayoutUtility.normalizeMargins(options.margins);
        var spacing = options.spacing || 0;
        var node;
        var nodeSize;
        var itemSize;
        var getItemSize;
        var lastSectionBeforeVisibleCell;
        var lastSectionBeforeVisibleCellOffset;
        var lastSectionBeforeVisibleCellLength;
        var lastSectionBeforeVisibleCellScrollLength;
        var lastSectionBeforeVisibleCellTopReached;
        var firstVisibleCell;
        var lastNode;
        var lastCellOffsetInFirstVisibleSection;
        var isSectionCallback = options.isSectionCallback;
        var bound;

        //
        // Sanity checks
        //
        if (spacing && typeof spacing !== 'number') {
            console.log('Famous-flex warning: ListLayout was initialized with a non-numeric spacing option. ' + // eslint-disable-line no-console
                'The CollectionLayout supports an array spacing argument, but the ListLayout does not.');
        }

        //
        // Reset size & translation
        //
        set.size[0] = size[0];
        set.size[1] = size[1];
        set.size[revDirection] -= (margins[1 - revDirection] + margins[3 - revDirection]);
        set.translate[0] = 0;
        set.translate[1] = 0;
        set.translate[2] = 0;
        set.translate[revDirection] = margins[direction ? 3 : 0];

        //
        // Determine item-size or use true=size
        //
        if ((options.itemSize === true) || !options.hasOwnProperty('itemSize')) {
            itemSize = true;
        }
        else if (options.itemSize instanceof Function) {
            getItemSize = options.itemSize;
        }
        else {
            itemSize = (options.itemSize === undefined) ? size[direction] : options.itemSize;
        }

        //
        // Determine leading/trailing margins
        //
        margin[0] = margins[direction ? 0 : 3];
        margin[1] = -margins[direction ? 2 : 1];

        //
        // Process all next nodes
        //
        offset = context.scrollOffset + margin[alignment];
        bound = context.scrollEnd + margin[alignment];
        while (offset < (bound + spacing)) {
            lastNode = node;
            node = context.next();
            if (!node) {
                break;
            }

            //
            // Get node size
            //
            nodeSize = getItemSize ? getItemSize(node.renderNode, context.size) : itemSize;
            nodeSize = (nodeSize === true) ? context.resolveSize(node, size)[direction] : nodeSize;

            //
            // Position node
            //
            set.size[direction] = nodeSize;
            set.translate[direction] = offset + (alignment ? spacing : 0);
            set.scrollLength = nodeSize + spacing;
            context.set(node, set);
            offset += set.scrollLength;

            //
            // Keep track of the last section before the first visible cell
            //
            if (isSectionCallback && isSectionCallback(node.renderNode)) {
                if ((set.translate[direction] <= margin[0]) && !lastSectionBeforeVisibleCellTopReached) {
                    lastSectionBeforeVisibleCellTopReached = true;
                    set.translate[direction] = margin[0];
                    context.set(node, set);
                }
                if (!firstVisibleCell) {
                    lastSectionBeforeVisibleCell = node;
                    lastSectionBeforeVisibleCellOffset = offset - nodeSize;
                    lastSectionBeforeVisibleCellLength = nodeSize;
                    lastSectionBeforeVisibleCellScrollLength = nodeSize;
                }
                else if (lastCellOffsetInFirstVisibleSection === undefined) {
                    lastCellOffsetInFirstVisibleSection = offset - nodeSize;
                }
            }
            else if (!firstVisibleCell && (offset >= 0)) {
                firstVisibleCell = node;
            }
        }
        if (lastNode && !node && !alignment) {
            set.scrollLength = nodeSize + margin[0] + -margin[1];
            context.set(lastNode, set);
        }

        //
        // Process previous nodes
        //
        lastNode = undefined;
        node = undefined;
        offset = context.scrollOffset + margin[alignment];
        bound = context.scrollStart + margin[alignment];
        while (offset > (bound - spacing)) {
            lastNode = node;
            node = context.prev();
            if (!node) {
                break;
            }

            //
            // Get node size
            //
            nodeSize = getItemSize ? getItemSize(node.renderNode, context.size) : itemSize;
            nodeSize = (nodeSize === true) ? context.resolveSize(node, size)[direction] : nodeSize;

            //
            // Position node
            //
            set.scrollLength = nodeSize + spacing;
            offset -= set.scrollLength;
            set.size[direction] = nodeSize;
            set.translate[direction] = offset + (alignment ? spacing : 0);
            context.set(node, set);

            //
            // Keep track of the last section before the first visible cell
            //
            if (isSectionCallback && isSectionCallback(node.renderNode)) {
                if ((set.translate[direction] <= margin[0]) && !lastSectionBeforeVisibleCellTopReached) {
                    lastSectionBeforeVisibleCellTopReached = true;
                    set.translate[direction] = margin[0];
                    context.set(node, set);
                }
                if (!lastSectionBeforeVisibleCell) {
                    lastSectionBeforeVisibleCell = node;
                    lastSectionBeforeVisibleCellOffset = offset;
                    lastSectionBeforeVisibleCellLength = nodeSize;
                    lastSectionBeforeVisibleCellScrollLength = set.scrollLength;
                }
            }
            else if ((offset + nodeSize) >= 0) {
                firstVisibleCell = node;
                if (lastSectionBeforeVisibleCell) {
                    lastCellOffsetInFirstVisibleSection = offset + nodeSize;
                }
                lastSectionBeforeVisibleCell = undefined;
            }
        }
        if (lastNode && !node && alignment) {
            set.scrollLength = nodeSize + margin[0] + -margin[1];
            context.set(lastNode, set);
            if (lastSectionBeforeVisibleCell === lastNode) {
                lastSectionBeforeVisibleCellScrollLength = set.scrollLength;
            }
        }

        //
        // When no first section is in the scrollable range, then
        // look back further in search for that section
        //
        if (isSectionCallback && !lastSectionBeforeVisibleCell) {
            node = context.prev();
            while (node) {
                if (isSectionCallback(node.renderNode)) {
                    lastSectionBeforeVisibleCell = node;
                    nodeSize = options.itemSize || context.resolveSize(node, size)[direction];
                    lastSectionBeforeVisibleCellOffset = offset - nodeSize;
                    lastSectionBeforeVisibleCellLength = nodeSize;
                    lastSectionBeforeVisibleCellScrollLength = undefined;
                    break;
                }
                else {
                    node = context.prev();
                }
            }
        }

        //
        // Reposition "last section before first visible cell" to the top of the layout
        //
        if (lastSectionBeforeVisibleCell) {
            var correctedOffset = Math.max(margin[0], lastSectionBeforeVisibleCellOffset);
            if ((lastCellOffsetInFirstVisibleSection !== undefined) &&
                (lastSectionBeforeVisibleCellLength > (lastCellOffsetInFirstVisibleSection - margin[0]))) {
                correctedOffset = ((lastCellOffsetInFirstVisibleSection - lastSectionBeforeVisibleCellLength));
            }
            set.size[direction] = lastSectionBeforeVisibleCellLength;
            set.translate[direction] = correctedOffset;
            set.scrollLength = lastSectionBeforeVisibleCellScrollLength;
            context.set(lastSectionBeforeVisibleCell, set);
        }
    }
function ListLayout(context, options) {

        // Local data
        var size = context.size;
        var direction = context.direction;
        var alignment = context.alignment;
        var revDirection = direction ? 0 : 1;
        var offset;
        var margins = LayoutUtility.normalizeMargins(options.margins);
        var spacing = options.spacing || 0;
        var node;
        var nodeSize;
        var itemSize;
        var getItemSize;
        var lastSectionBeforeVisibleCell;
        var lastSectionBeforeVisibleCellOffset;
        var lastSectionBeforeVisibleCellLength;
        var lastSectionBeforeVisibleCellScrollLength;
        var lastSectionBeforeVisibleCellTopReached;
        var firstVisibleCell;
        var lastNode;
        var lastCellOffsetInFirstVisibleSection;
        var isSectionCallback = options.isSectionCallback;
        var bound;

        //
        // Sanity checks
        //
        if (spacing && typeof spacing !== 'number') {
            console.log('Famous-flex warning: ListLayout was initialized with a non-numeric spacing option. ' + // eslint-disable-line no-console
                'The CollectionLayout supports an array spacing argument, but the ListLayout does not.');
        }

        //
        // Reset size & translation
        //
        set.size[0] = size[0];
        set.size[1] = size[1];
        set.size[revDirection] -= (margins[1 - revDirection] + margins[3 - revDirection]);
        set.translate[0] = 0;
        set.translate[1] = 0;
        set.translate[2] = 0;
        set.translate[revDirection] = margins[direction ? 3 : 0];

        //
        // Determine item-size or use true=size
        //
        if ((options.itemSize === true) || !options.hasOwnProperty('itemSize')) {
            itemSize = true;
        }
        else if (options.itemSize instanceof Function) {
            getItemSize = options.itemSize;
        }
        else {
            itemSize = (options.itemSize === undefined) ? size[direction] : options.itemSize;
        }

        //
        // Determine leading/trailing margins
        //
        margin[0] = margins[direction ? 0 : 3];
        margin[1] = -margins[direction ? 2 : 1];

        //
        // Process all next nodes
        //
        offset = context.scrollOffset + margin[alignment];
        bound = context.scrollEnd + margin[alignment];
        while (offset < (bound + spacing)) {
            lastNode = node;
            node = context.next();
            if (!node) {
                break;
            }

            //
            // Get node size
            //
            nodeSize = getItemSize ? getItemSize(node.renderNode, context.size) : itemSize;
            nodeSize = (nodeSize === true) ? context.resolveSize(node, size)[direction] : nodeSize;

            //
            // Position node
            //
            set.size[direction] = nodeSize;
            set.translate[direction] = offset + (alignment ? spacing : 0);
            set.scrollLength = nodeSize + spacing;
            context.set(node, set);
            offset += set.scrollLength;

            //
            // Keep track of the last section before the first visible cell
            //
            if (isSectionCallback && isSectionCallback(node.renderNode)) {
                if ((set.translate[direction] <= margin[0]) && !lastSectionBeforeVisibleCellTopReached) {
                    lastSectionBeforeVisibleCellTopReached = true;
                    set.translate[direction] = margin[0];
                    context.set(node, set);
                }
                if (!firstVisibleCell) {
                    lastSectionBeforeVisibleCell = node;
                    lastSectionBeforeVisibleCellOffset = offset - nodeSize;
                    lastSectionBeforeVisibleCellLength = nodeSize;
                    lastSectionBeforeVisibleCellScrollLength = nodeSize;
                }
                else if (lastCellOffsetInFirstVisibleSection === undefined) {
                    lastCellOffsetInFirstVisibleSection = offset - nodeSize;
                }
            }
            else if (!firstVisibleCell && (offset >= 0)) {
                firstVisibleCell = node;
            }
        }
        if (lastNode && !node && !alignment) {
            set.scrollLength = nodeSize + margin[0] + -margin[1];
            context.set(lastNode, set);
        }

        //
        // Process previous nodes
        //
        lastNode = undefined;
        node = undefined;
        offset = context.scrollOffset + margin[alignment];
        bound = context.scrollStart + margin[alignment];
        while (offset > (bound - spacing)) {
            lastNode = node;
            node = context.prev();
            if (!node) {
                break;
            }

            //
            // Get node size
            //
            nodeSize = getItemSize ? getItemSize(node.renderNode, context.size) : itemSize;
            nodeSize = (nodeSize === true) ? context.resolveSize(node, size)[direction] : nodeSize;

            //
            // Position node
            //
            set.scrollLength = nodeSize + spacing;
            offset -= set.scrollLength;
            set.size[direction] = nodeSize;
            set.translate[direction] = offset + (alignment ? spacing : 0);
            context.set(node, set);

            //
            // Keep track of the last section before the first visible cell
            //
            if (isSectionCallback && isSectionCallback(node.renderNode)) {
                if ((set.translate[direction] <= margin[0]) && !lastSectionBeforeVisibleCellTopReached) {
                    lastSectionBeforeVisibleCellTopReached = true;
                    set.translate[direction] = margin[0];
                    context.set(node, set);
                }
                if (!lastSectionBeforeVisibleCell) {
                    lastSectionBeforeVisibleCell = node;
                    lastSectionBeforeVisibleCellOffset = offset;
                    lastSectionBeforeVisibleCellLength = nodeSize;
                    lastSectionBeforeVisibleCellScrollLength = set.scrollLength;
                }
            }
            else if ((offset + nodeSize) >= 0) {
                firstVisibleCell = node;
                if (lastSectionBeforeVisibleCell) {
                    lastCellOffsetInFirstVisibleSection = offset + nodeSize;
                }
                lastSectionBeforeVisibleCell = undefined;
            }
        }
        if (lastNode && !node && alignment) {
            set.scrollLength = nodeSize + margin[0] + -margin[1];
            context.set(lastNode, set);
            if (lastSectionBeforeVisibleCell === lastNode) {
                lastSectionBeforeVisibleCellScrollLength = set.scrollLength;
            }
        }

        //
        // When no first section is in the scrollable range, then
        // look back further in search for that section
        //
        if (isSectionCallback && !lastSectionBeforeVisibleCell) {
            node = context.prev();
            while (node) {
                if (isSectionCallback(node.renderNode)) {
                    lastSectionBeforeVisibleCell = node;
                    nodeSize = options.itemSize || context.resolveSize(node, size)[direction];
                    lastSectionBeforeVisibleCellOffset = offset - nodeSize;
                    lastSectionBeforeVisibleCellLength = nodeSize;
                    lastSectionBeforeVisibleCellScrollLength = undefined;
                    break;
                }
                else {
                    node = context.prev();
                }
            }
        }

        //
        // Reposition "last section before first visible cell" to the top of the layout
        //
        if (lastSectionBeforeVisibleCell) {
            var correctedOffset = Math.max(margin[0], lastSectionBeforeVisibleCellOffset);
            if ((lastCellOffsetInFirstVisibleSection !== undefined) &&
                (lastSectionBeforeVisibleCellLength > (lastCellOffsetInFirstVisibleSection - margin[0]))) {
                correctedOffset = ((lastCellOffsetInFirstVisibleSection - lastSectionBeforeVisibleCellLength));
            }
            set.size[direction] = lastSectionBeforeVisibleCellLength;
            set.translate[direction] = correctedOffset;
            set.scrollLength = lastSectionBeforeVisibleCellScrollLength;
            context.set(lastSectionBeforeVisibleCell, set);
        }
    }
Layout function