Source: services/InfiniteScrollService.js

/**
 * Copyright (C) 2005-2016 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

/**
 * This service should be included on a page whenever you want to configure an
 * [AlfSortablePaginatedList]{@link module:alfresco/lists/AlfSortablePaginatedList} to be
 * configured to use infinite scrolling for handling pagination.
 * 
 * @module alfresco/services/InfiniteScrollService
 * @extends module:alfresco/services/BaseService
 * @mixes module:alfresco/documentlibrary/_AlfDocumentListTopicMixin
 * @mixes module:alfresco/core/_EventsMixin
 * @author david.webster@alfresco.com
 */
define(["dojo/_base/declare",
        "alfresco/services/BaseService",
        "alfresco/documentlibrary/_AlfDocumentListTopicMixin",
        "dojo/_base/lang",
        "alfresco/core/_EventsMixin",
        "alfresco/core/DomElementUtils"],
        function(declare, BaseService, _AlfDocumentListTopicMixin, lang, _EventsMixin, AlfDomUtils) {

   return declare([BaseService, _AlfDocumentListTopicMixin, _EventsMixin, AlfDomUtils], {

      /**
       * Used to keep track of the current status of the InfiniteScroll
       *
       * @instance
       * @type {boolean}
       * @default
       */
      dataloadInProgress: false,

      /**
       * Scroll tolerance in pixels.
       *
       * How close to the bottom of the page do we want to get before we request the next items?
       *
       * @instance
       * @type {int}
       * @default
       */
      scrollTolerance: 500,

      /**
       * By default, the [publishScrollEvents function]{@link module:alfresco/core/_EventsMixin#publishScrollEvents}
       * will be called in the constructor. If overridden and set to false then it will not, and should instead be
       * called manually later on.
       *
       * @instance
       * @type {boolean}
       * @default
       */
      _registerScrollListenerImmediately: true,

      /**
       * @instance
       * @listens scrollReturn
       * @listens requestFinishedTopic
       * @listens eventsScrollTopic
       * @since 1.0.32
       */
      registerSubscriptions: function alfresco_services_InfiniteScrollService__registerSubscriptions() {
         // Register the events listeners...
         if (this._registerScrollListenerImmediately)
         {
            this.publishScrollEvents();
         }
         
         // hook point to allow other widgets to let us know when they're done processing a scroll request.
         this.alfSubscribe(this.scrollReturn, lang.hitch(this, this.onScrollReturn));

         // Bind to this explicitly to reduce duplication in other widgets
         this.alfSubscribe(this.requestFinishedTopic, lang.hitch(this, this.onScrollReturn));

         // tie in to the events scroll module.
         this.alfSubscribe(this.eventsScrollTopic, lang.hitch(this, this.onEventsScroll));
      },

      /**
       * When the scroll event triggers, check location and pass on the warning that we're near the bottom of the page
       * sets dataloadInProgress to prevent duplicated triggers when the page is scrolled slowly.
       *
       * @instance
       * @param {object} payload
       */
      onEventsScroll: function alfresco_services_InfiniteScrollService__onEventsScroll(payload) {
         if (this.nearBottom(payload.node) && !this.dataloadInProgress) {
            this.dataloadInProgress = true;
            this.alfPublish(this.scrollNearBottom);
         }
      },

      /**
       * Called when infinite scroll request has been processed and allows us to trigger further scroll events
       *
       * @instance
       * @param {object} payload
       */
      onScrollReturn: function alfresco_services_InfiniteScrollService__onScrollReturn(/*jshint unused:false*/ payload) {
         this.dataloadInProgress = false;
      },

      /**
       * Determine if we're at or close to the bottom of the monitored node as defined by the
       * [scrollTolerance variable]{@link module:alfresco/services/InfiniteScrollService#scrollTolerance}.
       *
       * @instance
       * @param {Object} scrollNode The node being scrolled
       * @returns {boolean}
       */
      nearBottom: function alfresco_services_InfiniteScrollService__nearBottom(scrollNode) {
         var scrollHeight,
            clientHeight,
            scrollTop;
         if(scrollNode === window) {
            scrollHeight = document.body.scrollHeight;
            clientHeight = window.innerHeight;
            scrollTop = window.pageYOffset || (document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop;
         } else {
            scrollHeight= scrollNode.scrollHeight;
            clientHeight= scrollNode.clientHeight;
            scrollTop= scrollNode.scrollTop;
         }
         return (scrollHeight - clientHeight - scrollTop) < this.scrollTolerance;
      }
   });
});