%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /proc/985914/root/data/old/home/stash/atlassian-stash/static/util/
Upload File :
Create Path :
Current File : //proc/985914/root/data/old/home/stash/atlassian-stash/static/util/region-scroll-forwarder.js

define('util/region-scroll-forwarder', [
    'bacon',
    'jquery',
    'underscore',
    'util/bacon',
    'util/function'
],
/**
 * Forward scroll events from a source to one or many targets
 *
 * @exports util/region-scroll-forwarder
 */
function(
    Bacon,
    $,
    _,
    baconUtil,
    fn
) {
    "use strict";

    /**
     * Given a scroll property to listen to, determines the region that we're in and forwards scroll events.
     * When leaving a region, that region will be scrolled to the 'near' edge.
     * Allows for 'grouped' regions and will only scroll the 'nearest' region in each group to the edge.
     * This is useful when disjoint regions are actually forwarding to the same underlying object. E.g.
     *
     * ------------------- top
     * |
     * | region A - scroll the window while we're in this region
     * |
     * |-----
     * |
     * | region B - scroll an internal div while we're in this region
     * |
     * |-----
     * |
     * | region C - scroll the window while we're in this region
     * |
     * ------------------- bottom
     *
     * When a scroll event jumps from region A to C (e.g. user pressed PgDn), there is no need to scroll
     * A to the edge, because C will be affecting the window more accurately. But region B affects a different
     * element, so must be scrolled to the edge.
     *
     * @param {Bacon.Property} scrollProperty - Bacon Property that emits { top : number }
     * @param {Array<{setScrollTop : function(number), getHeight : function() : number, groupId : * }>} regions
     */
    function RegionScrollForwarder(scrollProperty, regions) {
        var destroyed = new Bacon.Bus();

        regions = _.map(regions, function(region) {
            var cachedHeight = null;
            return $.extend({}, region, {
                getCachedHeight : function() {
                    if (cachedHeight == null) {
                        cachedHeight = region.getHeight();
                    }
                    return cachedHeight;
                },
                invalidateCache : function() {
                    cachedHeight = null;
                }
            });
        });

        var latestForwardingInfo;

        // Subscribe to the scrollProperty so we can set the initial scrollTop for regions
        scrollProperty.subscribe(function(e) {
            // Only operate on the initial event.
            if (!e.isInitial()) {
                return;
            }

            var initialScroll = e.value().top;
            var offset = 0;
            var currentRegion;

            // set the scrollTop for all regions
            _.forEach(regions, function(region) {
                var height = region.getCachedHeight();
                var isBelow = offset + height >= initialScroll;

                if (!currentRegion && isBelow) {
                    currentRegion = region;
                    region.setScrollTop(initialScroll - offset);
                } else {
                    region.setScrollTop(isBelow ? 0 : height);
                }
                offset += height;
            });
        })(); // invoke to unsub straight away

        var forwardingInfo = scrollProperty
            .takeUntil(destroyed)
            .slidingWindow(2,2)
            .map(fn.spread(function(previousScrollInfo, scrollInfo) {
                var offset = 0;
                var prevRegion;
                var diff = scrollInfo.top - previousScrollInfo.top;
                var region;

                var offsetSoFar = 0;
                _.some(regions, function(currentRegion) {
                    var height = currentRegion.getCachedHeight();
                    if (!region && offsetSoFar + height >= scrollInfo.top) {
                        region = currentRegion;
                        offset = offsetSoFar;
                    }
                    if (!prevRegion && offsetSoFar + height >= previousScrollInfo.top) {
                        prevRegion = currentRegion;
                    }
                    offsetSoFar += height;
                    return region && prevRegion;
                });
                if (!region) {
                    throw new Error("No forwardee handles " + scrollInfo.top);
                }
                var info = {
                    scrollInfo : scrollInfo,
                    localTop : scrollInfo.top - offset,
                    diff : diff,
                    forwardTo : region,
                    previousRegion: prevRegion
                };
                return info;
            }));

        function scrollRegionsToEdge(regions, getEdge) {
            _.chain(regions)
                .filter(function(region, i, regions) { // unique by groupId, ensuring first region is chosen
                    return !_.some(regions.slice(0, i), function(otherInfo) {
                        return region.groupId && region.groupId === otherInfo.groupId;
                    });
                })
                .forEach(function(region) {
                    region.setScrollTop(getEdge(region) === 'bottom' ? region.getCachedHeight() : 0);
                });
        }

        var stopForwarding = forwardingInfo
            .onValue(function(forwardingInfo) {
                latestForwardingInfo = forwardingInfo;

                var forwardToRegion = forwardingInfo.previousRegion;
                // scroll all the regions between now and last time to their closest edge
                if (forwardToRegion !== forwardingInfo.forwardTo) {

                    var betweens = baconUtil.toArray(baconUtil.takeBetween(Bacon.fromArray(regions), {
                        start : forwardToRegion,
                        end : forwardingInfo.forwardTo,
                        startInclusive : true,
                        endInclusive : false
                    }));

                    var scrollingDown = forwardingInfo.diff > 0;
                    scrollRegionsToEdge(
                        scrollingDown ? betweens.reverse() : betweens,
                        fn.constant(scrollingDown ? 'bottom' : 'top'));
                }
                forwardingInfo.forwardTo.setScrollTop(forwardingInfo.localTop);
            });

        // Force all regions back to the right place - should have no affect unless offsets have changed.
        // Regions above the currently focused one are scrolled to the bottom.
        // Regions below the currently focused one are scrolled to the top.
        function resetPositions() {
            _.invoke(regions, 'invalidateCache');
            if (latestForwardingInfo) {
                var regionIndex = _.indexOf(regions, latestForwardingInfo.forwardTo);
                scrollRegionsToEdge(_.reject(regions, function(region) {
                    return region === latestForwardingInfo.forwardTo ||
                        (region.groupId && region.groupId === latestForwardingInfo.forwardTo.groupId);
                }), function(region) {
                    return _.indexOf(regions, region) < regionIndex ? 'bottom' : 'top';
                });
                latestForwardingInfo.forwardTo.setScrollTop(Math.min(latestForwardingInfo.localTop, latestForwardingInfo.forwardTo.getCachedHeight()));
            }
        }

        return {
            regionChanges : forwardingInfo.map('.forwardTo').skipDuplicates(),
            forwardingInfo : forwardingInfo,
            heightsChanged : resetPositions,
            destroy : function() {
                stopForwarding();
                destroyed.push(true);
                destroyed.end();
            }
        };
    }

    return RegionScrollForwarder;
});

Zerion Mini Shell 1.0