ctrl.init = function()

in kahuna/public/js/components/gu-lazy-table/gu-lazy-table.js [58:193]


    ctrl.init = function({items$, preloadedRows$, cellHeight$, cellMinWidth$,
                          containerWidth$, viewportHeight$, viewportTop$}) {
        const itemsCount$ = items$.map(items => items.length).distinctUntilChanged();

        const columns$ = max$(floor$(div$(containerWidth$, cellMinWidth$)), 1);
        const rows$ = ceil$(div$(itemsCount$, columns$));

        const cellWidth$ = floor$(div$(containerWidth$, columns$));

        const viewportBottom$ = add$(viewportTop$, viewportHeight$);

        const currentRowTop$    = round$(div$(viewportTop$,    cellHeight$));
        const currentRowBottom$ = round$(div$(viewportBottom$, cellHeight$));

        const loadedRowTop$ = max$(sub$(currentRowTop$, preloadedRows$), 0).
            distinctUntilChanged();
        const loadedRowBottom$ = combine$(currentRowBottom$, preloadedRows$, rows$,
                                          (currentRowBottom, preloadedRows, rows) => {
            return Math.min(currentRowBottom + preloadedRows, rows - 1);
        }).distinctUntilChanged();

        const loadedRangeStart$ = mult$(loadedRowTop$, columns$);
        const loadedRangeEnd$ = combine$(loadedRowBottom$, columns$, itemsCount$,
                                         (loadedRowBottom, columns, itemsCount) => {
            const endRowItemIndex = ((loadedRowBottom + 1) * columns) - 1;
            return Math.min(Math.max(endRowItemIndex, 0), itemsCount);
        });

        const rangeToLoad$ = combine$(
            items$, loadedRangeStart$, loadedRangeEnd$,
            (items, loadedRangeStart, loadedRangeEnd) => {
                const $start = findIndexFrom(items, undefined, loadedRangeStart);
                const $end   = findLastIndexFrom(items, undefined, loadedRangeEnd);
                return {$start, $end};
            }
        ).
            // Debounce range loading, which also helps discard
            // erroneous large ranges while combining
            // loadedRangeStart$ and loadedRangeEnd$ changes (one after the other)
            debounce(10).
            // Ignore if either end isn't set (whole range already loaded)
            filter(({$start, $end}) => $start !== -1 && $end !== -1).
            // Ignore if $start after $end (incomplete combine$ state)
            filter(({$start, $end}) => $start <= $end).
            // Max query size
            map(({$start, $end})=> {
              if (($end - $start) < maxSize) {
                return {$start, $end};
              }
              return {$start, $end: $start + maxSize - 1};
            }).
            distinctUntilChanged(({$start, $end}) => `${$start}-${$end}`);

        // Placeholders
        const placeholderExtraCount$ = mult$(columns$, preloadedRows$);
        const placeholderRangeStart$ = max$(sub$(loadedRangeStart$, placeholderExtraCount$), 0);
        const placeholderRangeEnd$ = min$(add$(loadedRangeEnd$, placeholderExtraCount$),
                                          sub$(itemsCount$, 1));

        const placeholderIndexes$ = combine$(
            placeholderRangeStart$, placeholderRangeEnd$,
            (placeholderRangeStart, placeholderRangeEnd) => {
                let indexes = range(placeholderRangeStart, placeholderRangeEnd);
                return Array.from(indexes);
            }
        );

        const viewHeight$ = mult$(rows$, cellHeight$);

        // Mutations needed here to access streams in this closure ;_;

        // Share subscriptions to these streams between all cells and
        // placeholders that register to their position

        const itemsShared$          = items$.shareReplay(1);
        const cellWidthShared$      = cellWidth$.shareReplay(1);
        const cellHeightShared$     = cellHeight$.shareReplay(1);
        const columnsShared$        = columns$.shareReplay(1);
        const preloadedRowsShared$  = preloadedRows$.shareReplay(1);
        const viewportTopShared$    = viewportTop$.shareReplay(1);
        const viewportBottomShared$ = viewportBottom$.shareReplay(1);

        ctrl.getItemPosition$ = createGetItemPosition$({
            items$:          itemsShared$,
            cellWidth$:      cellWidthShared$,
            cellHeight$:     cellHeightShared$,
            columns$:        columnsShared$,
            preloadedRows$:  preloadedRowsShared$,
            viewportTop$:    viewportTopShared$,
            viewportBottom$: viewportBottomShared$
        });

        ctrl.getCellPosition$ = createGetCellPosition$({
            cellWidth$:      cellWidthShared$,
            cellHeight$:     cellHeightShared$,
            columns$:        columnsShared$,
            preloadedRows$:  preloadedRowsShared$,
            viewportTop$:    viewportTopShared$,
            viewportBottom$: viewportBottomShared$
        });

        const rowsInViewport$ = max$(floor$(div$(viewportHeight$, cellHeight$)), 1);

        // Marginally satisfying pattern to convert from imperative to
        // reactive land
        const scrollCommands$ = Rx.Observable.create(observer => {
            ctrl.scrollPrevRow  = () => observer.onNext('prevRow');
            ctrl.scrollNextRow  = () => observer.onNext('nextRow');
            ctrl.scrollPrevPage = () => observer.onNext('prevPage');
            ctrl.scrollNextPage = () => observer.onNext('nextPage');
            ctrl.scrollStart    = () => observer.onNext('start');
            ctrl.scrollEnd      = () => observer.onNext('end');
        });

        const rowOffset$ = scrollCommands$.withLatestFrom(
            rowsInViewport$, currentRowTop$, rows$,
            (command, rowsInViewport, currentRowTop, rows) => {
                return {
                    prevRow:  - 1,
                    nextRow:  + 1,
                    prevPage: - rowsInViewport,
                    nextPage: + rowsInViewport,
                    start:    - currentRowTop,
                    end:      rows - currentRowTop
                }[command] || 0;
            });

        const newScrollTop$ = rowOffset$.withLatestFrom(currentRowTop$, cellHeight$,
                                                        (rowOffset, currentRowTop, cellHeight) => {
            return (currentRowTop + rowOffset) * cellHeight;
        });

        return {
            viewHeight$, rangeToLoad$, placeholderIndexes$, newScrollTop$
        };
    };