// history ( function( window, factory ) { // universal module definition if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./core'), require('fizzy-ui-utils'), ); } else { // browser global factory( window, window.InfiniteScroll, window.fizzyUIUtils, ); } }( window, function factory( window, InfiniteScroll, utils ) { let proto = InfiniteScroll.prototype; Object.assign( InfiniteScroll.defaults, { history: 'replace', // historyTitle: false, } ); let link = document.createElement('a'); // ----- create/destroy ----- // InfiniteScroll.create.history = function() { if ( !this.options.history ) return; // check for same origin link.href = this.getAbsolutePath(); // MS Edge does not have origin on link // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12236493/ let linkOrigin = link.origin || link.protocol + '//' + link.host; let isSameOrigin = linkOrigin == location.origin; if ( !isSameOrigin ) { console.error( '[InfiniteScroll] cannot set history with different origin: ' + `${link.origin} on ${location.origin} . History behavior disabled.` ); return; } // two ways to handle changing history if ( this.options.append ) { this.createHistoryAppend(); } else { this.createHistoryPageLoad(); } }; proto.createHistoryAppend = function() { this.updateMeasurements(); this.updateScroller(); // array of scroll positions of appended pages this.scrollPages = [ // first page { top: 0, path: location.href, title: document.title, }, ]; this.scrollPage = this.scrollPages[0]; // events this.scrollHistoryHandler = this.onScrollHistory.bind( this ); this.unloadHandler = this.onUnload.bind( this ); this.scroller.addEventListener( 'scroll', this.scrollHistoryHandler ); this.on( 'append', this.onAppendHistory ); this.bindHistoryAppendEvents( true ); }; proto.bindHistoryAppendEvents = function( isBind ) { let addRemove = isBind ? 'addEventListener' : 'removeEventListener'; this.scroller[ addRemove ]( 'scroll', this.scrollHistoryHandler ); window[ addRemove ]( 'unload', this.unloadHandler ); }; proto.createHistoryPageLoad = function() { this.on( 'load', this.onPageLoadHistory ); }; InfiniteScroll.destroy.history = proto.destroyHistory = function() { let isHistoryAppend = this.options.history && this.options.append; if ( isHistoryAppend ) { this.bindHistoryAppendEvents( false ); } }; // ----- append history ----- // proto.onAppendHistory = function( response, path, items ) { // do not proceed if no items. #779 if ( !items || !items.length ) return; let firstItem = items[0]; let elemScrollY = this.getElementScrollY( firstItem ); // resolve path link.href = path; // add page data to hash this.scrollPages.push({ top: elemScrollY, path: link.href, title: response.title, }); }; proto.getElementScrollY = function( elem ) { if ( this.options.elementScroll ) { return elem.offsetTop - this.top; } else { let rect = elem.getBoundingClientRect(); return rect.top + window.scrollY; } }; proto.onScrollHistory = function() { // cycle through positions, find biggest without going over let scrollPage = this.getClosestScrollPage(); // set history if changed if ( scrollPage != this.scrollPage ) { this.scrollPage = scrollPage; this.setHistory( scrollPage.title, scrollPage.path ); } }; utils.debounceMethod( InfiniteScroll, 'onScrollHistory', 150 ); proto.getClosestScrollPage = function() { let scrollViewY; if ( this.options.elementScroll ) { scrollViewY = this.scroller.scrollTop + this.scroller.clientHeight / 2; } else { scrollViewY = window.scrollY + this.windowHeight / 2; } let scrollPage; for ( let page of this.scrollPages ) { if ( page.top >= scrollViewY ) break; scrollPage = page; } return scrollPage; }; proto.setHistory = function( title, path ) { let optHistory = this.options.history; let historyMethod = optHistory && history[ optHistory + 'State' ]; if ( !historyMethod ) return; history[ optHistory + 'State' ]( null, title, path ); if ( this.options.historyTitle ) document.title = title; this.dispatchEvent( 'history', null, [ title, path ] ); }; // scroll to top to prevent initial scroll-reset after page refresh // https://stackoverflow.com/a/18633915/182183 proto.onUnload = function() { if ( this.scrollPage.top === 0 ) return; // calculate where scroll position would be on refresh let scrollY = window.scrollY - this.scrollPage.top + this.top; // disable scroll event before setting scroll #679 this.destroyHistory(); scrollTo( 0, scrollY ); }; // ----- load history ----- // // update URL proto.onPageLoadHistory = function( response, path ) { this.setHistory( response.title, path ); }; // -------------------------- -------------------------- // return InfiniteScroll; } ) );