| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -17,6 +17,7 @@ limitations under the License.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				var React = require("react");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				var ReactDOM = require("react-dom");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				var GeminiScrollbar = require('react-gemini-scrollbar');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import { StickyContainer } from 'react-sticky';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				import Promise from 'bluebird';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				var KeyCode = require('../../KeyCode');
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -77,111 +78,48 @@ if (DEBUG_SCROLL) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * scroll down further. If stickyBottom is disabled, we just save the scroll
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 * offset as normal.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				 */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    displayName: 'ScrollPanel',
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				export default class ScrollPanel extends StickyContainer {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    propTypes: {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* stickyBottom: if set to true, then once the user hits the bottom of
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * the list, any new children added to the list will cause the list to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * scroll down to show the new element, rather than preserving the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * existing view.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        stickyBottom: React.PropTypes.bool,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    constructor() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        super();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.onResize = this.onResize.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.onScroll = this.onScroll.bind(this);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* startAtBottom: if set to true, the view is assumed to start
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * scrolled to the bottom.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * XXX: It's likley this is unecessary and can be derived from
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * stickyBottom, but I'm adding an extra parameter to ensure
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * behaviour stays the same for other uses of ScrollPanel.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * If so, let's remove this parameter down the line.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        startAtBottom: React.PropTypes.bool,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* onFillRequest(backwards): a callback which is called on scroll when
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * the user nears the start (backwards = true) or end (backwards =
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * false) of the list.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         *
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * This should return a promise; no more calls will be made until the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * promise completes.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         *
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * The promise should resolve to true if there is more data to be
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * retrieved in this direction (in which case onFillRequest may be
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * called again immediately), or false if there is no more data in this
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * directon (at this time) - which will stop the pagination cycle until
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * the user scrolls again.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        onFillRequest: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* onUnfillRequest(backwards): a callback which is called on scroll when
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * there are children elements that are far out of view and could be removed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * without causing pagination to occur.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         *
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * This function should accept a boolean, which is true to indicate the back/top
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * of the panel and false otherwise, and a scroll token, which refers to the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * first element to remove if removing from the front/bottom, and last element
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * to remove if removing from the back/top.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        onUnfillRequest: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* onScroll: a callback which is called whenever any scroll happens.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        onScroll: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* onResize: a callback which is called whenever the Gemini scroll
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         * panel is resized
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        onResize: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* className: classnames to add to the top-level div
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        className: React.PropTypes.string,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* style: styles to add to the top-level div
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				         */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        style: React.PropTypes.object,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    getDefaultProps: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            stickyBottom: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            startAtBottom: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            onFillRequest: function(backwards) { return Promise.resolve(false); },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            onUnfillRequest: function(backwards, scrollToken) {},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            onScroll: function() {},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentWillMount: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentWillMount() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._pendingFillRequests = {b: null, f: null};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.resetScrollState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentDidMount: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentDidMount() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.checkFillState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentDidUpdate: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentDidUpdate() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // after adding event tiles, we may need to tweak the scroll (either to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // keep at the bottom of the timeline, or to maintain the view after
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // adding events to the top).
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // This will also re-check the fill state, in case the paginate was inadequate
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.checkScroll();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentWillUnmount: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    componentWillUnmount() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // set a boolean to say we've been unmounted, which any pending
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // promises can use to throw away their results.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // (We could use isMounted(), but facebook have deprecated that.)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.unmounted = true;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onScroll: function(ev) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onScroll(ev) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var sn = this._getScrollNode();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debuglog("Scroll event: offset now:", sn.scrollTop,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                 "_lastSetScroll:", this._lastSetScroll);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.node = sn;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.notifySubscribers(ev);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // Sometimes we see attempts to write to scrollTop essentially being
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // ignored. (Or rather, it is successfully written, but on the next
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // scroll event, it's been reset again).
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -217,27 +155,27 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.props.onScroll(ev);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.checkFillState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onResize: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onResize() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.props.onResize();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.checkScroll();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.refs.geminiPanel.forceUpdate();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // after an update to the contents of the panel, check that the scroll is
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // where it ought to be, and set off pagination requests if necessary.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    checkScroll: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    checkScroll() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._restoreSavedScrollState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.checkFillState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // return true if the content is fully scrolled down right now; else false.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // note that this is independent of the 'stuckAtBottom' state - it is simply
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // about whether the the content is scrolled down right now, irrespective of
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // whether it will stay that way when the children update.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    isAtBottom: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    isAtBottom() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var sn = this._getScrollNode();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // there seems to be some bug with flexbox/gemini/chrome/richvdh's
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -247,7 +185,7 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // that we're at the bottom when we're still a few pixels off.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return sn.scrollHeight - Math.ceil(sn.scrollTop) <= sn.clientHeight + 3;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // returns the vertical height in the given direction that can be removed from
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // the content box (which has a height of scrollHeight, see checkFillState) without
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -280,17 +218,17 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //   |#########|   -                                   |
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //   |#########|                                       |
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    //   `---------'                                       -
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _getExcessHeight: function(backwards) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _getExcessHeight(backwards) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var sn = this._getScrollNode();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (backwards) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return sn.scrollTop - sn.clientHeight - UNPAGINATION_PADDING;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return sn.scrollHeight - (sn.scrollTop + 2*sn.clientHeight) - UNPAGINATION_PADDING;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // check the scroll state and send out backfill requests if necessary.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    checkFillState: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    checkFillState() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (this.unmounted) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -329,10 +267,10 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            // need to forward-fill
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            this._maybeFill(false);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // check if unfilling is possible and send an unfill request if necessary
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _checkUnfillState: function(backwards) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _checkUnfillState(backwards) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        let excessHeight = this._getExcessHeight(backwards);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (excessHeight <= 0) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            return;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -373,10 +311,10 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                this.props.onUnfillRequest(backwards, markerScrollToken);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }, UNFILL_REQUEST_DEBOUNCE_MS);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // check if there is already a pending fill request. If not, set one off.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _maybeFill: function(backwards) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _maybeFill(backwards) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var dir = backwards ? 'b' : 'f';
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (this._pendingFillRequests[dir]) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debuglog("ScrollPanel: Already a "+dir+" fill in progress - not starting another");
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -408,7 +346,7 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                this.checkFillState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }).done();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* get the current scroll state. This returns an object with the following
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * properties:
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -424,9 +362,9 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     *   the number of pixels the bottom of the tracked child is above the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     *   bottom of the scroll panel.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    getScrollState: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    getScrollState() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return this.scrollState;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* reset the saved scroll state.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     *
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -440,46 +378,46 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * no use if no children exist yet, or if you are about to replace the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * child list.)
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    resetScrollState: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    resetScrollState() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this.scrollState = {stuckAtBottom: this.props.startAtBottom};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /**
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * jump to the top of the content.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollToTop: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollToTop() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._setScrollTop(0);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._saveScrollState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /**
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * jump to the bottom of the content.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollToBottom: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollToBottom() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // the easiest way to make sure that the scroll state is correctly
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // saved is to do the scroll, then save the updated state. (Calculating
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // it ourselves is hard, and we can't rely on an onScroll callback
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // happening, since there may be no user-visible change here).
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._setScrollTop(Number.MAX_VALUE);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._saveScrollState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /**
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * Page up/down.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     *
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * mult: -1 to page up, +1 to page down
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollRelative: function(mult) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollRelative(mult) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var scrollNode = this._getScrollNode();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var delta = mult * scrollNode.clientHeight * 0.5;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._setScrollTop(scrollNode.scrollTop + delta);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._saveScrollState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /**
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * Scroll up/down in response to a scroll key
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    handleScrollKey: function(ev) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    handleScrollKey(ev) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        switch (ev.keyCode) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            case KeyCode.PAGE_UP:
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                if (!ev.ctrlKey && !ev.shiftKey && !ev.altKey && !ev.metaKey) {
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -505,7 +443,7 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                break;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* Scroll the panel to bring the DOM node with the scroll token
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * `scrollToken` into view.
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -518,7 +456,7 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * node (specifically, the bottom of it) will be positioned. If omitted, it
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * defaults to 0.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollToToken: function(scrollToken, pixelOffset, offsetBase) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    scrollToToken(scrollToken, pixelOffset, offsetBase) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        pixelOffset = pixelOffset || 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        offsetBase = offsetBase || 0;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -540,11 +478,11 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // ... then make it so.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        this._restoreSavedScrollState();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // set the scrollTop attribute appropriately to position the given child at the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    // given offset in the window. A helper for _restoreSavedScrollState.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _scrollToToken: function(scrollToken, pixelOffset) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _scrollToToken(scrollToken, pixelOffset) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        /* find the dom node with the right scrolltoken */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var node;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var messages = this.refs.itemlist.children;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -576,9 +514,9 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            this._setScrollTop(scrollNode.scrollTop + scrollDelta);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _saveScrollState: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _saveScrollState() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (this.props.stickyBottom && this.isAtBottom()) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            this.scrollState = { stuckAtBottom: true };
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debuglog("ScrollPanel: Saved scroll state", this.scrollState);
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -616,9 +554,9 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        } else {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            debuglog("ScrollPanel: unable to save scroll state: found no children in the viewport");
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _restoreSavedScrollState: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _restoreSavedScrollState() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var scrollState = this.scrollState;
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var scrollNode = this._getScrollNode();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -628,9 +566,9 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            this._scrollToToken(scrollState.trackedScrollToken,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                               scrollState.pixelOffset);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _setScrollTop: function(scrollTop) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _setScrollTop(scrollTop) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var scrollNode = this._getScrollNode();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        var prevScroll = scrollNode.scrollTop;
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -652,12 +590,12 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        debuglog("ScrollPanel: set scrollTop:", scrollNode.scrollTop,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                 "requested:", scrollTop,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                 "_lastSetScroll:", this._lastSetScroll);
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* get the DOM node which has the scrollTop property we care about for our
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * message panel.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _getScrollNode: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    _getScrollNode() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        if (this.unmounted) {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            // this shouldn't happen, but when it does, turn the NPE into
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            // something more meaningful.
 | 
			
		
		
	
	
		
			
				
					| 
						
					 | 
				
			
			 | 
			 | 
			
				@ -665,21 +603,91 @@ module.exports = React.createClass({
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return this.refs.geminiPanel.scrollbar.getViewElement();
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    render: function() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    render() {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // TODO: the classnames on the div and ol could do with being updated to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // reflect the fact that we don't necessarily contain a list of messages.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        // it's not obvious why we have a separate div and ol anyway.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return (<GeminiScrollbar autoshow={true} ref="geminiPanel"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                onScroll={this.onScroll} onResize={this.onResize}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                className={this.props.className} style={this.props.style}>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    <div className="mx_RoomView_messageListWrapper">
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        <ol ref="itemlist" className="mx_RoomView_MessageList" aria-live="polite">
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                            {this.props.children}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        </ol>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    </div>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                </GeminiScrollbar>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				               );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				});
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        return (
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            <GeminiScrollbar autoshow={true} ref="geminiPanel"
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            onScroll={this.onScroll} onResize={this.onResize}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            className={this.props.className} style={this.props.style}>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                <div className="mx_RoomView_messageListWrapper">
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    <ol ref="itemlist" className="mx_RoomView_MessageList" aria-live="polite">
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                        {this.props.children}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                    </ol>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				                </div>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				            </GeminiScrollbar>
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				        );
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    }
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				}
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				ScrollPanel.propTypes = {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* stickyBottom: if set to true, then once the user hits the bottom of
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * the list, any new children added to the list will cause the list to
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * scroll down to show the new element, rather than preserving the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * existing view.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    stickyBottom: React.PropTypes.bool,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* startAtBottom: if set to true, the view is assumed to start
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * scrolled to the bottom.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * XXX: It's likley this is unecessary and can be derived from
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * stickyBottom, but I'm adding an extra parameter to ensure
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * behaviour stays the same for other uses of ScrollPanel.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * If so, let's remove this parameter down the line.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    startAtBottom: React.PropTypes.bool,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* onFillRequest(backwards): a callback which is called on scroll when
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * the user nears the start (backwards = true) or end (backwards =
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * false) of the list.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     *
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * This should return a promise; no more calls will be made until the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * promise completes.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     *
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * The promise should resolve to true if there is more data to be
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * retrieved in this direction (in which case onFillRequest may be
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * called again immediately), or false if there is no more data in this
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * directon (at this time) - which will stop the pagination cycle until
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * the user scrolls again.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onFillRequest: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* onUnfillRequest(backwards): a callback which is called on scroll when
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * there are children elements that are far out of view and could be removed
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * without causing pagination to occur.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     *
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * This function should accept a boolean, which is true to indicate the back/top
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * of the panel and false otherwise, and a scroll token, which refers to the
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * first element to remove if removing from the front/bottom, and last element
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * to remove if removing from the back/top.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onUnfillRequest: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* onScroll: a callback which is called whenever any scroll happens.
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onScroll: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* onResize: a callback which is called whenever the Gemini scroll
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     * panel is resized
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onResize: React.PropTypes.func,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* className: classnames to add to the top-level div
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    className: React.PropTypes.string,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    /* style: styles to add to the top-level div
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				     */
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    style: React.PropTypes.object,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				};
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				ScrollPanel.defaultProps = {
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    stickyBottom: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    startAtBottom: true,
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onFillRequest: function(backwards) { return Promise.resolve(false); },
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onUnfillRequest: function(backwards, scrollToken) {},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				    onScroll: function() {},
 | 
			
		
		
	
		
			
				 | 
				 | 
			
			 | 
			 | 
			
				};
 | 
			
		
		
	
	
		
			
				
					| 
						 
							
							
							
						 
					 | 
				
			
			 | 
			 | 
			
				
 
 |