Source: components/filter/filter-result/filter-result-element/pagination/product-pagination-infinite.js

import jQ from 'jquery';
import ProductPagination from "./product-pagination";
import FilterApi from "../../../../../api/filter-api";
import Selector from "../../../../../helpers/selector";
import Class from "../../../../../helpers/class";
import Globals from "../../../../../helpers/globals";
import Settings from "../../../../../helpers/settings";
import Utils from "../../../../../helpers/utils";
import BoostPFS from "../../../../../boost-pfs";
import SearchResultPanels from '../search-result-panels';
import SearchResultPanelItem from '../search-result-panel-item';

/**
 * Product pagination - load more
 * @extends ProductPagination
 */
class ProductPaginationInfinite extends ProductPagination {
	/**
	 * @constructs
	 */
	constructor() {
		super();
		this.$element = jQ(Selector.bottomPagination);
		// Add more settings
		jQ.extend(this.settings, {
			positionShowInfiniteLoading: Settings.getSettingValue('general.positionShowInfiniteLoading'),
			sessionStorageCurrentNextPage: Settings.getSettingValue('general.sessionStorageCurrentNextPage'),
			sessionStoragePreviousPageEvent: Settings.getSettingValue('general.sessionStoragePreviousPageEvent')
		});
	}

	/**
	 * Get te raw HTML template for pagination - infinite loading
	 * @param {String} tempType 
	 */
	getTemplate() {
		return `
			<div class="{{class.productLoadMore}}-loading" style="display: none;">
				<div class="{{class.productLoadMore}}-icon"></div>
			</div>
		`;
	}

	/**
	 * Replace the brackets in raw html template with proper values for infinite loading
	 * @returns {String} HTML string
	 */
	compileTemplate() {
		return this.getTemplate()
			.replace(/{{class.productLoadMore}}/g, Class.productLoadMore);
	}

	/**
	 * Return whether or not the pagination - infinite loading is rendered
	 */
	isRender() {
		return this.data !== null && SearchResultPanels.isPanelActive(SearchResultPanelItem.Enum.PRODUCT);
	}

	/**
	 * Render the pagination - infinite loading
	 */
	render() {
		// Blank the default pagination
		this.$element.empty();
		// Show the pagination block
		this.$element.show();
		// Add loading element
		if (this.$loadMore.find(Selector.loadMoreLoading).length == 0) {
			var loadingHtml = this.compileTemplate();
			this.$loadMore.prepend(loadingHtml);
			this.$loadMore.show();
		}
		// hide loading
		this.hideLoading();
	}

	/**
	 * Bind the event on infinite loading
	 */
	bindEvents() {
		// Previous page data
		if (Utils.isLoadPreviousPagePaginationType()) {
			this.nextPage = parseInt(window.sessionStorage.getItem(this.settings.sessionStorageCurrentNextPage));
		} else {
			this.nextPage = Globals.queryParams.page;
		}

		// Check if total products is greater than limit per page * curent page,
		// do the infinite loading function, else do nothing
		if (this.totalProduct > (this.settings.limit * this.nextPage)) {
			// Using scrolling to prevent callling onscroll twice
			this.scrolling = false;
			// Onscroll event, when windown reaches the bottom of product list
			this.scrollToBottom = false;
			// Check if pagination exists, do the infinite function
			if (this.$element.length > 0) {
				// When scroll down, do the infinite function
				jQ(window).on('scroll', this._onScrollEvent.bind(this));
			}
		}
	}

	/**
	 * Bind the scroll event on Window to load more product
	 * @param {Object} event - DOM event
	 */
	_onScrollEvent(event) {
		event.preventDefault();
		event.stopPropagation();
		// Ignore load more if the product list is loading
		if (jQ(Selector.products).hasClass(Class.productWrapLoading) || !SearchResultPanels.isPanelActive(SearchResultPanelItem.Enum.PRODUCT)) return false;

		if (this._isScrollToBottom()) {
			this._loadMoreProducts();
		}
	}

	_isScrollToBottom() {
		// get height of scroll bar
		var scrollbarHeight = jQ(window).height() * (jQ(window).height() / jQ(document).outerHeight());

		// get position of the pagination bottom
		var maxPos = parseInt(this.$element.offset().top);
		var maxScroll = parseInt(jQ(window).scrollTop()) + scrollbarHeight + parseInt(this.settings.positionShowInfiniteLoading);
		if ((jQ(window).scrollTop() + jQ(window).height() + scrollbarHeight) >= jQ(document).outerHeight() - 100) {
			this.scrollToBottom = true;
		}
		// Return true if scroll to the end of product list
		return (!this.scrolling && this.data.products.length > 0) && (maxScroll >= maxPos || (maxScroll < maxPos && this.scrollToBottom));
	}

	_loadMoreProducts() {
		// Show Loading
		this.showLoading();
		// Prevent scrolling twice
		this.scrolling = true;
		// Next page
		this.nextPage++;
		// Send request if current page is smaller or equal to total number of pages
		var totalPage = Math.ceil(this.totalProduct / this.settings.limit);
		if (this.nextPage <= totalPage) {
			Globals.internalClick = true;
			// Set limit param
			FilterApi.setParam('limit', this.settings.limit);
			// Set new page param
			FilterApi.setParam('page', this.nextPage);
			if (Utils.FilterResult.isAdvancedPaginationType()) {
				// If the previous page feature is activated
				if (Utils.isLoadPreviousPagePaginationType()) {
					// Save the previous page data
					window.sessionStorage.setItem(this.settings.sessionStorageCurrentNextPage, this.nextPage);
					window.sessionStorage.setItem(this.settings.sessionStoragePreviousPageEvent, 0);
				}
				// Apply filter with new navigation url
				FilterApi.applyFilter('page');
			} else {
				// Apply filter without new navigation url
				var filter = BoostPFS.instance.filter;
				FilterApi.getFilterData('page', filter.setData.bind(filter));
			}
		}
	}
}

export default ProductPaginationInfinite;