1
0
Fork 0
mirror of https://github.com/DanielnetoDotCom/YouPHPTube synced 2025-10-03 17:59:55 +02:00

Improving the get restream credentials

This commit is contained in:
DanieL 2022-05-20 16:22:54 -03:00
parent 654dda115a
commit 56cb1fd5cb
6058 changed files with 1166166 additions and 1430809 deletions

View file

@ -1,81 +1,81 @@
// button
( 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 ) {
// -------------------------- InfiniteScrollButton -------------------------- //
class InfiniteScrollButton {
constructor( element, infScroll ) {
this.element = element;
this.infScroll = infScroll;
// events
this.clickHandler = this.onClick.bind( this );
this.element.addEventListener( 'click', this.clickHandler );
infScroll.on( 'request', this.disable.bind( this ) );
infScroll.on( 'load', this.enable.bind( this ) );
infScroll.on( 'error', this.hide.bind( this ) );
infScroll.on( 'last', this.hide.bind( this ) );
}
onClick( event ) {
event.preventDefault();
this.infScroll.loadNextPage();
}
enable() {
this.element.removeAttribute('disabled');
}
disable() {
this.element.disabled = 'disabled';
}
hide() {
this.element.style.display = 'none';
}
destroy() {
this.element.removeEventListener( 'click', this.clickHandler );
}
}
// -------------------------- InfiniteScroll methods -------------------------- //
// InfiniteScroll.defaults.button = null;
InfiniteScroll.create.button = function() {
let buttonElem = utils.getQueryElement( this.options.button );
if ( buttonElem ) {
this.button = new InfiniteScrollButton( buttonElem, this );
}
};
InfiniteScroll.destroy.button = function() {
if ( this.button ) this.button.destroy();
};
// -------------------------- -------------------------- //
InfiniteScroll.Button = InfiniteScrollButton;
return InfiniteScroll;
} ) );
// button
( 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 ) {
// -------------------------- InfiniteScrollButton -------------------------- //
class InfiniteScrollButton {
constructor( element, infScroll ) {
this.element = element;
this.infScroll = infScroll;
// events
this.clickHandler = this.onClick.bind( this );
this.element.addEventListener( 'click', this.clickHandler );
infScroll.on( 'request', this.disable.bind( this ) );
infScroll.on( 'load', this.enable.bind( this ) );
infScroll.on( 'error', this.hide.bind( this ) );
infScroll.on( 'last', this.hide.bind( this ) );
}
onClick( event ) {
event.preventDefault();
this.infScroll.loadNextPage();
}
enable() {
this.element.removeAttribute('disabled');
}
disable() {
this.element.disabled = 'disabled';
}
hide() {
this.element.style.display = 'none';
}
destroy() {
this.element.removeEventListener( 'click', this.clickHandler );
}
}
// -------------------------- InfiniteScroll methods -------------------------- //
// InfiniteScroll.defaults.button = null;
InfiniteScroll.create.button = function() {
let buttonElem = utils.getQueryElement( this.options.button );
if ( buttonElem ) {
this.button = new InfiniteScrollButton( buttonElem, this );
}
};
InfiniteScroll.destroy.button = function() {
if ( this.button ) this.button.destroy();
};
// -------------------------- -------------------------- //
InfiniteScroll.Button = InfiniteScrollButton;
return InfiniteScroll;
} ) );

View file

@ -1,371 +1,371 @@
// core
( function( window, factory ) {
// universal module definition
if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
window,
require('ev-emitter'),
require('fizzy-ui-utils'),
);
} else {
// browser global
window.InfiniteScroll = factory(
window,
window.EvEmitter,
window.fizzyUIUtils,
);
}
}( window, function factory( window, EvEmitter, utils ) {
let jQuery = window.jQuery;
// internal store of all InfiniteScroll intances
let instances = {};
function InfiniteScroll( element, options ) {
let queryElem = utils.getQueryElement( element );
if ( !queryElem ) {
console.error( 'Bad element for InfiniteScroll: ' + ( queryElem || element ) );
return;
}
element = queryElem;
// do not initialize twice on same element
if ( element.infiniteScrollGUID ) {
let instance = instances[ element.infiniteScrollGUID ];
instance.option( options );
return instance;
}
this.element = element;
// options
this.options = { ...InfiniteScroll.defaults };
this.option( options );
// add jQuery
if ( jQuery ) {
this.$element = jQuery( this.element );
}
this.create();
}
// defaults
InfiniteScroll.defaults = {
// path: null,
// hideNav: null,
// debug: false,
};
// create & destroy methods
InfiniteScroll.create = {};
InfiniteScroll.destroy = {};
let proto = InfiniteScroll.prototype;
// inherit EvEmitter
Object.assign( proto, EvEmitter.prototype );
// -------------------------- -------------------------- //
// globally unique identifiers
let GUID = 0;
proto.create = function() {
// create core
// add id for InfiniteScroll.data
let id = this.guid = ++GUID;
this.element.infiniteScrollGUID = id; // expando
instances[ id ] = this; // associate via id
// properties
this.pageIndex = 1; // default to first page
this.loadCount = 0;
this.updateGetPath();
// bail if getPath not set, or returns falsey #776
let hasPath = this.getPath && this.getPath();
if ( !hasPath ) {
console.error('Disabling InfiniteScroll');
return;
}
this.updateGetAbsolutePath();
this.log( 'initialized', [ this.element.className ] );
this.callOnInit();
// create features
for ( let method in InfiniteScroll.create ) {
InfiniteScroll.create[ method ].call( this );
}
};
proto.option = function( opts ) {
Object.assign( this.options, opts );
};
// call onInit option, used for binding events on init
proto.callOnInit = function() {
let onInit = this.options.onInit;
if ( onInit ) {
onInit.call( this, this );
}
};
// ----- events ----- //
proto.dispatchEvent = function( type, event, args ) {
this.log( type, args );
let emitArgs = event ? [ event ].concat( args ) : args;
this.emitEvent( type, emitArgs );
// trigger jQuery event
if ( !jQuery || !this.$element ) {
return;
}
// namespace jQuery event
type += '.infiniteScroll';
let $event = type;
if ( event ) {
// create jQuery event
/* eslint-disable-next-line new-cap */
let jQEvent = jQuery.Event( event );
jQEvent.type = type;
$event = jQEvent;
}
this.$element.trigger( $event, args );
};
let loggers = {
initialized: ( className ) => `on ${className}`,
request: ( path ) => `URL: ${path}`,
load: ( response, path ) => `${response.title || ''}. URL: ${path}`,
error: ( error, path ) => `${error}. URL: ${path}`,
append: ( response, path, items ) => `${items.length} items. URL: ${path}`,
last: ( response, path ) => `URL: ${path}`,
history: ( title, path ) => `URL: ${path}`,
pageIndex: function( index, origin ) {
return `current page determined to be: ${index} from ${origin}`;
},
};
// log events
proto.log = function( type, args ) {
if ( !this.options.debug ) return;
let message = `[InfiniteScroll] ${type}`;
let logger = loggers[ type ];
if ( logger ) message += '. ' + logger.apply( this, args );
console.log( message );
};
// -------------------------- methods used amoung features -------------------------- //
proto.updateMeasurements = function() {
this.windowHeight = window.innerHeight;
let rect = this.element.getBoundingClientRect();
this.top = rect.top + window.scrollY;
};
proto.updateScroller = function() {
let elementScroll = this.options.elementScroll;
if ( !elementScroll ) {
// default, use window
this.scroller = window;
return;
}
// if true, set to element, otherwise use option
this.scroller = elementScroll === true ? this.element :
utils.getQueryElement( elementScroll );
if ( !this.scroller ) {
throw new Error(`Unable to find elementScroll: ${elementScroll}`);
}
};
// -------------------------- page path -------------------------- //
proto.updateGetPath = function() {
let optPath = this.options.path;
if ( !optPath ) {
console.error(`InfiniteScroll path option required. Set as: ${optPath}`);
return;
}
// function
let type = typeof optPath;
if ( type == 'function' ) {
this.getPath = optPath;
return;
}
// template string: '/pages/{{#}}.html'
let templateMatch = type == 'string' && optPath.match('{{#}}');
if ( templateMatch ) {
this.updateGetPathTemplate( optPath );
return;
}
// selector: '.next-page-selector'
this.updateGetPathSelector( optPath );
};
proto.updateGetPathTemplate = function( optPath ) {
// set getPath with template string
this.getPath = () => {
let nextIndex = this.pageIndex + 1;
return optPath.replace( '{{#}}', nextIndex );
};
// get pageIndex from location
// convert path option into regex to look for pattern in location
// escape query (?) in url, allows for parsing GET parameters
let regexString = optPath
.replace( /(\\\?|\?)/, '\\?' )
.replace( '{{#}}', '(\\d\\d?\\d?)' );
let templateRe = new RegExp( regexString );
let match = location.href.match( templateRe );
if ( match ) {
this.pageIndex = parseInt( match[1], 10 );
this.log( 'pageIndex', [ this.pageIndex, 'template string' ] );
}
};
let pathRegexes = [
// WordPress & Tumblr - example.com/page/2
// Jekyll - example.com/page2
/^(.*?\/?page\/?)(\d\d?\d?)(.*?$)/,
// Drupal - example.com/?page=1
/^(.*?\/?\?page=)(\d\d?\d?)(.*?$)/,
// catch all, last occurence of a number
/(.*?)(\d\d?\d?)(?!.*\d)(.*?$)/,
];
// try matching href to pathRegexes patterns
let getPathParts = InfiniteScroll.getPathParts = function( href ) {
if ( !href ) return;
for ( let regex of pathRegexes ) {
let match = href.match( regex );
if ( match ) {
let [ , begin, index, end ] = match;
return { begin, index, end };
}
}
};
proto.updateGetPathSelector = function( optPath ) {
// parse href of link: '.next-page-link'
let hrefElem = document.querySelector( optPath );
if ( !hrefElem ) {
console.error(`Bad InfiniteScroll path option. Next link not found: ${optPath}`);
return;
}
let href = hrefElem.getAttribute('href');
let pathParts = getPathParts( href );
if ( !pathParts ) {
console.error(`InfiniteScroll unable to parse next link href: ${href}`);
return;
}
let { begin, index, end } = pathParts;
this.isPathSelector = true; // flag for checkLastPage()
this.getPath = () => begin + ( this.pageIndex + 1 ) + end;
// get pageIndex from href
this.pageIndex = parseInt( index, 10 ) - 1;
this.log( 'pageIndex', [ this.pageIndex, 'next link' ] );
};
proto.updateGetAbsolutePath = function() {
let path = this.getPath();
// path doesn't start with http or /
let isAbsolute = path.match( /^http/ ) || path.match( /^\// );
if ( isAbsolute ) {
this.getAbsolutePath = this.getPath;
return;
}
let { pathname } = location;
// query parameter #829. example.com/?pg=2
let isQuery = path.match( /^\?/ );
// /foo/bar/index.html => /foo/bar
let directory = pathname.substring( 0, pathname.lastIndexOf('/') );
let pathStart = isQuery ? pathname : directory + '/';
this.getAbsolutePath = () => pathStart + this.getPath();
};
// -------------------------- nav -------------------------- //
// hide navigation
InfiniteScroll.create.hideNav = function() {
let nav = utils.getQueryElement( this.options.hideNav );
if ( !nav ) return;
nav.style.display = 'none';
this.nav = nav;
};
InfiniteScroll.destroy.hideNav = function() {
if ( this.nav ) this.nav.style.display = '';
};
// -------------------------- destroy -------------------------- //
proto.destroy = function() {
this.allOff(); // remove all event listeners
// call destroy methods
for ( let method in InfiniteScroll.destroy ) {
InfiniteScroll.destroy[ method ].call( this );
}
delete this.element.infiniteScrollGUID;
delete instances[ this.guid ];
// remove jQuery data. #807
if ( jQuery && this.$element ) {
jQuery.removeData( this.element, 'infiniteScroll' );
}
};
// -------------------------- utilities -------------------------- //
// https://remysharp.com/2010/07/21/throttling-function-calls
InfiniteScroll.throttle = function( fn, threshold ) {
threshold = threshold || 200;
let last, timeout;
return function() {
let now = +new Date();
let args = arguments;
let trigger = () => {
last = now;
fn.apply( this, args );
};
if ( last && now < last + threshold ) {
// hold on to it
clearTimeout( timeout );
timeout = setTimeout( trigger, threshold );
} else {
trigger();
}
};
};
InfiniteScroll.data = function( elem ) {
elem = utils.getQueryElement( elem );
let id = elem && elem.infiniteScrollGUID;
return id && instances[ id ];
};
// set internal jQuery, for Webpack + jQuery v3
InfiniteScroll.setJQuery = function( jqry ) {
jQuery = jqry;
};
// -------------------------- setup -------------------------- //
utils.htmlInit( InfiniteScroll, 'infinite-scroll' );
// add noop _init method for jQuery Bridget. #768
proto._init = function() {};
let { jQueryBridget } = window;
if ( jQuery && jQueryBridget ) {
jQueryBridget( 'infiniteScroll', InfiniteScroll, jQuery );
}
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );
// core
( function( window, factory ) {
// universal module definition
if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
window,
require('ev-emitter'),
require('fizzy-ui-utils'),
);
} else {
// browser global
window.InfiniteScroll = factory(
window,
window.EvEmitter,
window.fizzyUIUtils,
);
}
}( window, function factory( window, EvEmitter, utils ) {
let jQuery = window.jQuery;
// internal store of all InfiniteScroll intances
let instances = {};
function InfiniteScroll( element, options ) {
let queryElem = utils.getQueryElement( element );
if ( !queryElem ) {
console.error( 'Bad element for InfiniteScroll: ' + ( queryElem || element ) );
return;
}
element = queryElem;
// do not initialize twice on same element
if ( element.infiniteScrollGUID ) {
let instance = instances[ element.infiniteScrollGUID ];
instance.option( options );
return instance;
}
this.element = element;
// options
this.options = { ...InfiniteScroll.defaults };
this.option( options );
// add jQuery
if ( jQuery ) {
this.$element = jQuery( this.element );
}
this.create();
}
// defaults
InfiniteScroll.defaults = {
// path: null,
// hideNav: null,
// debug: false,
};
// create & destroy methods
InfiniteScroll.create = {};
InfiniteScroll.destroy = {};
let proto = InfiniteScroll.prototype;
// inherit EvEmitter
Object.assign( proto, EvEmitter.prototype );
// -------------------------- -------------------------- //
// globally unique identifiers
let GUID = 0;
proto.create = function() {
// create core
// add id for InfiniteScroll.data
let id = this.guid = ++GUID;
this.element.infiniteScrollGUID = id; // expando
instances[ id ] = this; // associate via id
// properties
this.pageIndex = 1; // default to first page
this.loadCount = 0;
this.updateGetPath();
// bail if getPath not set, or returns falsey #776
let hasPath = this.getPath && this.getPath();
if ( !hasPath ) {
console.error('Disabling InfiniteScroll');
return;
}
this.updateGetAbsolutePath();
this.log( 'initialized', [ this.element.className ] );
this.callOnInit();
// create features
for ( let method in InfiniteScroll.create ) {
InfiniteScroll.create[ method ].call( this );
}
};
proto.option = function( opts ) {
Object.assign( this.options, opts );
};
// call onInit option, used for binding events on init
proto.callOnInit = function() {
let onInit = this.options.onInit;
if ( onInit ) {
onInit.call( this, this );
}
};
// ----- events ----- //
proto.dispatchEvent = function( type, event, args ) {
this.log( type, args );
let emitArgs = event ? [ event ].concat( args ) : args;
this.emitEvent( type, emitArgs );
// trigger jQuery event
if ( !jQuery || !this.$element ) {
return;
}
// namespace jQuery event
type += '.infiniteScroll';
let $event = type;
if ( event ) {
// create jQuery event
/* eslint-disable-next-line new-cap */
let jQEvent = jQuery.Event( event );
jQEvent.type = type;
$event = jQEvent;
}
this.$element.trigger( $event, args );
};
let loggers = {
initialized: ( className ) => `on ${className}`,
request: ( path ) => `URL: ${path}`,
load: ( response, path ) => `${response.title || ''}. URL: ${path}`,
error: ( error, path ) => `${error}. URL: ${path}`,
append: ( response, path, items ) => `${items.length} items. URL: ${path}`,
last: ( response, path ) => `URL: ${path}`,
history: ( title, path ) => `URL: ${path}`,
pageIndex: function( index, origin ) {
return `current page determined to be: ${index} from ${origin}`;
},
};
// log events
proto.log = function( type, args ) {
if ( !this.options.debug ) return;
let message = `[InfiniteScroll] ${type}`;
let logger = loggers[ type ];
if ( logger ) message += '. ' + logger.apply( this, args );
console.log( message );
};
// -------------------------- methods used amoung features -------------------------- //
proto.updateMeasurements = function() {
this.windowHeight = window.innerHeight;
let rect = this.element.getBoundingClientRect();
this.top = rect.top + window.scrollY;
};
proto.updateScroller = function() {
let elementScroll = this.options.elementScroll;
if ( !elementScroll ) {
// default, use window
this.scroller = window;
return;
}
// if true, set to element, otherwise use option
this.scroller = elementScroll === true ? this.element :
utils.getQueryElement( elementScroll );
if ( !this.scroller ) {
throw new Error(`Unable to find elementScroll: ${elementScroll}`);
}
};
// -------------------------- page path -------------------------- //
proto.updateGetPath = function() {
let optPath = this.options.path;
if ( !optPath ) {
console.error(`InfiniteScroll path option required. Set as: ${optPath}`);
return;
}
// function
let type = typeof optPath;
if ( type == 'function' ) {
this.getPath = optPath;
return;
}
// template string: '/pages/{{#}}.html'
let templateMatch = type == 'string' && optPath.match('{{#}}');
if ( templateMatch ) {
this.updateGetPathTemplate( optPath );
return;
}
// selector: '.next-page-selector'
this.updateGetPathSelector( optPath );
};
proto.updateGetPathTemplate = function( optPath ) {
// set getPath with template string
this.getPath = () => {
let nextIndex = this.pageIndex + 1;
return optPath.replace( '{{#}}', nextIndex );
};
// get pageIndex from location
// convert path option into regex to look for pattern in location
// escape query (?) in url, allows for parsing GET parameters
let regexString = optPath
.replace( /(\\\?|\?)/, '\\?' )
.replace( '{{#}}', '(\\d\\d?\\d?)' );
let templateRe = new RegExp( regexString );
let match = location.href.match( templateRe );
if ( match ) {
this.pageIndex = parseInt( match[1], 10 );
this.log( 'pageIndex', [ this.pageIndex, 'template string' ] );
}
};
let pathRegexes = [
// WordPress & Tumblr - example.com/page/2
// Jekyll - example.com/page2
/^(.*?\/?page\/?)(\d\d?\d?)(.*?$)/,
// Drupal - example.com/?page=1
/^(.*?\/?\?page=)(\d\d?\d?)(.*?$)/,
// catch all, last occurence of a number
/(.*?)(\d\d?\d?)(?!.*\d)(.*?$)/,
];
// try matching href to pathRegexes patterns
let getPathParts = InfiniteScroll.getPathParts = function( href ) {
if ( !href ) return;
for ( let regex of pathRegexes ) {
let match = href.match( regex );
if ( match ) {
let [ , begin, index, end ] = match;
return { begin, index, end };
}
}
};
proto.updateGetPathSelector = function( optPath ) {
// parse href of link: '.next-page-link'
let hrefElem = document.querySelector( optPath );
if ( !hrefElem ) {
console.error(`Bad InfiniteScroll path option. Next link not found: ${optPath}`);
return;
}
let href = hrefElem.getAttribute('href');
let pathParts = getPathParts( href );
if ( !pathParts ) {
console.error(`InfiniteScroll unable to parse next link href: ${href}`);
return;
}
let { begin, index, end } = pathParts;
this.isPathSelector = true; // flag for checkLastPage()
this.getPath = () => begin + ( this.pageIndex + 1 ) + end;
// get pageIndex from href
this.pageIndex = parseInt( index, 10 ) - 1;
this.log( 'pageIndex', [ this.pageIndex, 'next link' ] );
};
proto.updateGetAbsolutePath = function() {
let path = this.getPath();
// path doesn't start with http or /
let isAbsolute = path.match( /^http/ ) || path.match( /^\// );
if ( isAbsolute ) {
this.getAbsolutePath = this.getPath;
return;
}
let { pathname } = location;
// query parameter #829. example.com/?pg=2
let isQuery = path.match( /^\?/ );
// /foo/bar/index.html => /foo/bar
let directory = pathname.substring( 0, pathname.lastIndexOf('/') );
let pathStart = isQuery ? pathname : directory + '/';
this.getAbsolutePath = () => pathStart + this.getPath();
};
// -------------------------- nav -------------------------- //
// hide navigation
InfiniteScroll.create.hideNav = function() {
let nav = utils.getQueryElement( this.options.hideNav );
if ( !nav ) return;
nav.style.display = 'none';
this.nav = nav;
};
InfiniteScroll.destroy.hideNav = function() {
if ( this.nav ) this.nav.style.display = '';
};
// -------------------------- destroy -------------------------- //
proto.destroy = function() {
this.allOff(); // remove all event listeners
// call destroy methods
for ( let method in InfiniteScroll.destroy ) {
InfiniteScroll.destroy[ method ].call( this );
}
delete this.element.infiniteScrollGUID;
delete instances[ this.guid ];
// remove jQuery data. #807
if ( jQuery && this.$element ) {
jQuery.removeData( this.element, 'infiniteScroll' );
}
};
// -------------------------- utilities -------------------------- //
// https://remysharp.com/2010/07/21/throttling-function-calls
InfiniteScroll.throttle = function( fn, threshold ) {
threshold = threshold || 200;
let last, timeout;
return function() {
let now = +new Date();
let args = arguments;
let trigger = () => {
last = now;
fn.apply( this, args );
};
if ( last && now < last + threshold ) {
// hold on to it
clearTimeout( timeout );
timeout = setTimeout( trigger, threshold );
} else {
trigger();
}
};
};
InfiniteScroll.data = function( elem ) {
elem = utils.getQueryElement( elem );
let id = elem && elem.infiniteScrollGUID;
return id && instances[ id ];
};
// set internal jQuery, for Webpack + jQuery v3
InfiniteScroll.setJQuery = function( jqry ) {
jQuery = jqry;
};
// -------------------------- setup -------------------------- //
utils.htmlInit( InfiniteScroll, 'infinite-scroll' );
// add noop _init method for jQuery Bridget. #768
proto._init = function() {};
let { jQueryBridget } = window;
if ( jQuery && jQueryBridget ) {
jQueryBridget( 'infiniteScroll', InfiniteScroll, jQuery );
}
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );

View file

@ -1,184 +1,184 @@
// 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;
} ) );
// 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;
} ) );

View file

@ -1,28 +1,28 @@
/*!
* Infinite Scroll v4.0.1
* Automatically add next page
*
* Licensed GPLv3 for open source use
* or Infinite Scroll Commercial License for commercial use
*
* https://infinite-scroll.com
* Copyright 2018-2020 Metafizzy
*/
( function( window, factory ) {
// universal module definition
if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
require('./core'),
require('./page-load'),
require('./scroll-watch'),
require('./history'),
require('./button'),
require('./status'),
);
}
} )( window, function factory( InfiniteScroll ) {
return InfiniteScroll;
} );
/*!
* Infinite Scroll v4.0.1
* Automatically add next page
*
* Licensed GPLv3 for open source use
* or Infinite Scroll Commercial License for commercial use
*
* https://infinite-scroll.com
* Copyright 2018-2020 Metafizzy
*/
( function( window, factory ) {
// universal module definition
if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
require('./core'),
require('./page-load'),
require('./scroll-watch'),
require('./history'),
require('./button'),
require('./status'),
);
}
} )( window, function factory( InfiniteScroll ) {
return InfiniteScroll;
} );

View file

@ -1,276 +1,276 @@
// page-load
( function( window, factory ) {
// universal module definition
if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
window,
require('./core'),
);
} else {
// browser global
factory(
window,
window.InfiniteScroll,
);
}
}( window, function factory( window, InfiniteScroll ) {
let proto = InfiniteScroll.prototype;
Object.assign( InfiniteScroll.defaults, {
// append: false,
loadOnScroll: true,
checkLastPage: true,
responseBody: 'text',
domParseResponse: true,
// prefill: false,
// outlayer: null,
} );
InfiniteScroll.create.pageLoad = function() {
this.canLoad = true;
this.on( 'scrollThreshold', this.onScrollThresholdLoad );
this.on( 'load', this.checkLastPage );
if ( this.options.outlayer ) {
this.on( 'append', this.onAppendOutlayer );
}
};
proto.onScrollThresholdLoad = function() {
if ( this.options.loadOnScroll ) this.loadNextPage();
};
let domParser = new DOMParser();
proto.loadNextPage = function() {
if ( this.isLoading || !this.canLoad ) return;
let { responseBody, domParseResponse, fetchOptions } = this.options;
let path = this.getAbsolutePath();
this.isLoading = true;
if ( typeof fetchOptions == 'function' ) fetchOptions = fetchOptions();
let fetchPromise = fetch( path, fetchOptions )
.then( ( response ) => {
if ( !response.ok ) {
let error = new Error( response.statusText );
this.onPageError( error, path, response );
return { response };
}
return response[ responseBody ]().then( ( body ) => {
let canDomParse = responseBody == 'text' && domParseResponse;
if ( canDomParse ) {
body = domParser.parseFromString( body, 'text/html' );
}
if ( response.status == 204 ) {
this.lastPageReached( body, path );
return { body, response };
} else {
return this.onPageLoad( body, path, response );
}
} );
} )
.catch( ( error ) => {
this.onPageError( error, path );
} );
this.dispatchEvent( 'request', null, [ path, fetchPromise ] );
return fetchPromise;
};
proto.onPageLoad = function( body, path, response ) {
// done loading if not appending
if ( !this.options.append ) {
this.isLoading = false;
}
this.pageIndex++;
this.loadCount++;
this.dispatchEvent( 'load', null, [ body, path, response ] );
return this.appendNextPage( body, path, response );
};
proto.appendNextPage = function( body, path, response ) {
let { append, responseBody, domParseResponse } = this.options;
// do not append json
let isDocument = responseBody == 'text' && domParseResponse;
if ( !isDocument || !append ) return { body, response };
let items = body.querySelectorAll( append );
let promiseValue = { body, response, items };
// last page hit if no items. #840
if ( !items || !items.length ) {
this.lastPageReached( body, path );
return promiseValue;
}
let fragment = getItemsFragment( items );
let appendReady = () => {
this.appendItems( items, fragment );
this.isLoading = false;
this.dispatchEvent( 'append', null, [ body, path, items, response ] );
return promiseValue;
};
// TODO add hook for option to trigger appendReady
if ( this.options.outlayer ) {
return this.appendOutlayerItems( fragment, appendReady );
} else {
return appendReady();
}
};
proto.appendItems = function( items, fragment ) {
if ( !items || !items.length ) return;
// get fragment if not provided
fragment = fragment || getItemsFragment( items );
refreshScripts( fragment );
this.element.appendChild( fragment );
};
function getItemsFragment( items ) {
// add items to fragment
let fragment = document.createDocumentFragment();
if ( items ) fragment.append( ...items );
return fragment;
}
// replace <script>s with copies so they load
// <script>s added by InfiniteScroll will not load
// similar to https://stackoverflow.com/questions/610995
function refreshScripts( fragment ) {
let scripts = fragment.querySelectorAll('script');
for ( let script of scripts ) {
let freshScript = document.createElement('script');
// copy attributes
let attrs = script.attributes;
for ( let attr of attrs ) {
freshScript.setAttribute( attr.name, attr.value );
}
// copy inner script code. #718, #782
freshScript.innerHTML = script.innerHTML;
script.parentNode.replaceChild( freshScript, script );
}
}
// ----- outlayer ----- //
proto.appendOutlayerItems = function( fragment, appendReady ) {
let imagesLoaded = InfiniteScroll.imagesLoaded || window.imagesLoaded;
if ( !imagesLoaded ) {
console.error('[InfiniteScroll] imagesLoaded required for outlayer option');
this.isLoading = false;
return;
}
// append once images loaded
return new Promise( function( resolve ) {
imagesLoaded( fragment, function() {
let bodyResponse = appendReady();
resolve( bodyResponse );
} );
} );
};
proto.onAppendOutlayer = function( response, path, items ) {
this.options.outlayer.appended( items );
};
// ----- checkLastPage ----- //
// check response for next element
proto.checkLastPage = function( body, path ) {
let { checkLastPage, path: pathOpt } = this.options;
if ( !checkLastPage ) return;
// if path is function, check if next path is truthy
if ( typeof pathOpt == 'function' ) {
let nextPath = this.getPath();
if ( !nextPath ) {
this.lastPageReached( body, path );
return;
}
}
// get selector from checkLastPage or path option
let selector;
if ( typeof checkLastPage == 'string' ) {
selector = checkLastPage;
} else if ( this.isPathSelector ) {
// path option is selector string
selector = pathOpt;
}
// check last page for selector
// bail if no selector or not document response
if ( !selector || !body.querySelector ) return;
// check if response has selector
let nextElem = body.querySelector( selector );
if ( !nextElem ) this.lastPageReached( body, path );
};
proto.lastPageReached = function( body, path ) {
this.canLoad = false;
this.dispatchEvent( 'last', null, [ body, path ] );
};
// ----- error ----- //
proto.onPageError = function( error, path, response ) {
this.isLoading = false;
this.canLoad = false;
this.dispatchEvent( 'error', null, [ error, path, response ] );
return error;
};
// -------------------------- prefill -------------------------- //
InfiniteScroll.create.prefill = function() {
if ( !this.options.prefill ) return;
let append = this.options.append;
if ( !append ) {
console.error(`append option required for prefill. Set as :${append}`);
return;
}
this.updateMeasurements();
this.updateScroller();
this.isPrefilling = true;
this.on( 'append', this.prefill );
this.once( 'error', this.stopPrefill );
this.once( 'last', this.stopPrefill );
this.prefill();
};
proto.prefill = function() {
let distance = this.getPrefillDistance();
this.isPrefilling = distance >= 0;
if ( this.isPrefilling ) {
this.log('prefill');
this.loadNextPage();
} else {
this.stopPrefill();
}
};
proto.getPrefillDistance = function() {
// element scroll
if ( this.options.elementScroll ) {
return this.scroller.clientHeight - this.scroller.scrollHeight;
}
// window
return this.windowHeight - this.element.clientHeight;
};
proto.stopPrefill = function() {
this.log('stopPrefill');
this.off( 'append', this.prefill );
};
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );
// page-load
( function( window, factory ) {
// universal module definition
if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
window,
require('./core'),
);
} else {
// browser global
factory(
window,
window.InfiniteScroll,
);
}
}( window, function factory( window, InfiniteScroll ) {
let proto = InfiniteScroll.prototype;
Object.assign( InfiniteScroll.defaults, {
// append: false,
loadOnScroll: true,
checkLastPage: true,
responseBody: 'text',
domParseResponse: true,
// prefill: false,
// outlayer: null,
} );
InfiniteScroll.create.pageLoad = function() {
this.canLoad = true;
this.on( 'scrollThreshold', this.onScrollThresholdLoad );
this.on( 'load', this.checkLastPage );
if ( this.options.outlayer ) {
this.on( 'append', this.onAppendOutlayer );
}
};
proto.onScrollThresholdLoad = function() {
if ( this.options.loadOnScroll ) this.loadNextPage();
};
let domParser = new DOMParser();
proto.loadNextPage = function() {
if ( this.isLoading || !this.canLoad ) return;
let { responseBody, domParseResponse, fetchOptions } = this.options;
let path = this.getAbsolutePath();
this.isLoading = true;
if ( typeof fetchOptions == 'function' ) fetchOptions = fetchOptions();
let fetchPromise = fetch( path, fetchOptions )
.then( ( response ) => {
if ( !response.ok ) {
let error = new Error( response.statusText );
this.onPageError( error, path, response );
return { response };
}
return response[ responseBody ]().then( ( body ) => {
let canDomParse = responseBody == 'text' && domParseResponse;
if ( canDomParse ) {
body = domParser.parseFromString( body, 'text/html' );
}
if ( response.status == 204 ) {
this.lastPageReached( body, path );
return { body, response };
} else {
return this.onPageLoad( body, path, response );
}
} );
} )
.catch( ( error ) => {
this.onPageError( error, path );
} );
this.dispatchEvent( 'request', null, [ path, fetchPromise ] );
return fetchPromise;
};
proto.onPageLoad = function( body, path, response ) {
// done loading if not appending
if ( !this.options.append ) {
this.isLoading = false;
}
this.pageIndex++;
this.loadCount++;
this.dispatchEvent( 'load', null, [ body, path, response ] );
return this.appendNextPage( body, path, response );
};
proto.appendNextPage = function( body, path, response ) {
let { append, responseBody, domParseResponse } = this.options;
// do not append json
let isDocument = responseBody == 'text' && domParseResponse;
if ( !isDocument || !append ) return { body, response };
let items = body.querySelectorAll( append );
let promiseValue = { body, response, items };
// last page hit if no items. #840
if ( !items || !items.length ) {
this.lastPageReached( body, path );
return promiseValue;
}
let fragment = getItemsFragment( items );
let appendReady = () => {
this.appendItems( items, fragment );
this.isLoading = false;
this.dispatchEvent( 'append', null, [ body, path, items, response ] );
return promiseValue;
};
// TODO add hook for option to trigger appendReady
if ( this.options.outlayer ) {
return this.appendOutlayerItems( fragment, appendReady );
} else {
return appendReady();
}
};
proto.appendItems = function( items, fragment ) {
if ( !items || !items.length ) return;
// get fragment if not provided
fragment = fragment || getItemsFragment( items );
refreshScripts( fragment );
this.element.appendChild( fragment );
};
function getItemsFragment( items ) {
// add items to fragment
let fragment = document.createDocumentFragment();
if ( items ) fragment.append( ...items );
return fragment;
}
// replace <script>s with copies so they load
// <script>s added by InfiniteScroll will not load
// similar to https://stackoverflow.com/questions/610995
function refreshScripts( fragment ) {
let scripts = fragment.querySelectorAll('script');
for ( let script of scripts ) {
let freshScript = document.createElement('script');
// copy attributes
let attrs = script.attributes;
for ( let attr of attrs ) {
freshScript.setAttribute( attr.name, attr.value );
}
// copy inner script code. #718, #782
freshScript.innerHTML = script.innerHTML;
script.parentNode.replaceChild( freshScript, script );
}
}
// ----- outlayer ----- //
proto.appendOutlayerItems = function( fragment, appendReady ) {
let imagesLoaded = InfiniteScroll.imagesLoaded || window.imagesLoaded;
if ( !imagesLoaded ) {
console.error('[InfiniteScroll] imagesLoaded required for outlayer option');
this.isLoading = false;
return;
}
// append once images loaded
return new Promise( function( resolve ) {
imagesLoaded( fragment, function() {
let bodyResponse = appendReady();
resolve( bodyResponse );
} );
} );
};
proto.onAppendOutlayer = function( response, path, items ) {
this.options.outlayer.appended( items );
};
// ----- checkLastPage ----- //
// check response for next element
proto.checkLastPage = function( body, path ) {
let { checkLastPage, path: pathOpt } = this.options;
if ( !checkLastPage ) return;
// if path is function, check if next path is truthy
if ( typeof pathOpt == 'function' ) {
let nextPath = this.getPath();
if ( !nextPath ) {
this.lastPageReached( body, path );
return;
}
}
// get selector from checkLastPage or path option
let selector;
if ( typeof checkLastPage == 'string' ) {
selector = checkLastPage;
} else if ( this.isPathSelector ) {
// path option is selector string
selector = pathOpt;
}
// check last page for selector
// bail if no selector or not document response
if ( !selector || !body.querySelector ) return;
// check if response has selector
let nextElem = body.querySelector( selector );
if ( !nextElem ) this.lastPageReached( body, path );
};
proto.lastPageReached = function( body, path ) {
this.canLoad = false;
this.dispatchEvent( 'last', null, [ body, path ] );
};
// ----- error ----- //
proto.onPageError = function( error, path, response ) {
this.isLoading = false;
this.canLoad = false;
this.dispatchEvent( 'error', null, [ error, path, response ] );
return error;
};
// -------------------------- prefill -------------------------- //
InfiniteScroll.create.prefill = function() {
if ( !this.options.prefill ) return;
let append = this.options.append;
if ( !append ) {
console.error(`append option required for prefill. Set as :${append}`);
return;
}
this.updateMeasurements();
this.updateScroller();
this.isPrefilling = true;
this.on( 'append', this.prefill );
this.once( 'error', this.stopPrefill );
this.once( 'last', this.stopPrefill );
this.prefill();
};
proto.prefill = function() {
let distance = this.getPrefillDistance();
this.isPrefilling = distance >= 0;
if ( this.isPrefilling ) {
this.log('prefill');
this.loadNextPage();
} else {
this.stopPrefill();
}
};
proto.getPrefillDistance = function() {
// element scroll
if ( this.options.elementScroll ) {
return this.scroller.clientHeight - this.scroller.scrollHeight;
}
// window
return this.windowHeight - this.element.clientHeight;
};
proto.stopPrefill = function() {
this.log('stopPrefill');
this.off( 'append', this.prefill );
};
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );

View file

@ -1,97 +1,97 @@
// scroll-watch
( 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;
// default options
Object.assign( InfiniteScroll.defaults, {
scrollThreshold: 400,
// elementScroll: null,
} );
InfiniteScroll.create.scrollWatch = function() {
// events
this.pageScrollHandler = this.onPageScroll.bind( this );
this.resizeHandler = this.onResize.bind( this );
let scrollThreshold = this.options.scrollThreshold;
let isEnable = scrollThreshold || scrollThreshold === 0;
if ( isEnable ) this.enableScrollWatch();
};
InfiniteScroll.destroy.scrollWatch = function() {
this.disableScrollWatch();
};
proto.enableScrollWatch = function() {
if ( this.isScrollWatching ) return;
this.isScrollWatching = true;
this.updateMeasurements();
this.updateScroller();
// TODO disable after error?
this.on( 'last', this.disableScrollWatch );
this.bindScrollWatchEvents( true );
};
proto.disableScrollWatch = function() {
if ( !this.isScrollWatching ) return;
this.bindScrollWatchEvents( false );
delete this.isScrollWatching;
};
proto.bindScrollWatchEvents = function( isBind ) {
let addRemove = isBind ? 'addEventListener' : 'removeEventListener';
this.scroller[ addRemove ]( 'scroll', this.pageScrollHandler );
window[ addRemove ]( 'resize', this.resizeHandler );
};
proto.onPageScroll = InfiniteScroll.throttle( function() {
let distance = this.getBottomDistance();
if ( distance <= this.options.scrollThreshold ) {
this.dispatchEvent('scrollThreshold');
}
} );
proto.getBottomDistance = function() {
let bottom, scrollY;
if ( this.options.elementScroll ) {
bottom = this.scroller.scrollHeight;
scrollY = this.scroller.scrollTop + this.scroller.clientHeight;
} else {
bottom = this.top + this.element.clientHeight;
scrollY = window.scrollY + this.windowHeight;
}
return bottom - scrollY;
};
proto.onResize = function() {
this.updateMeasurements();
};
utils.debounceMethod( InfiniteScroll, 'onResize', 150 );
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );
// scroll-watch
( 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;
// default options
Object.assign( InfiniteScroll.defaults, {
scrollThreshold: 400,
// elementScroll: null,
} );
InfiniteScroll.create.scrollWatch = function() {
// events
this.pageScrollHandler = this.onPageScroll.bind( this );
this.resizeHandler = this.onResize.bind( this );
let scrollThreshold = this.options.scrollThreshold;
let isEnable = scrollThreshold || scrollThreshold === 0;
if ( isEnable ) this.enableScrollWatch();
};
InfiniteScroll.destroy.scrollWatch = function() {
this.disableScrollWatch();
};
proto.enableScrollWatch = function() {
if ( this.isScrollWatching ) return;
this.isScrollWatching = true;
this.updateMeasurements();
this.updateScroller();
// TODO disable after error?
this.on( 'last', this.disableScrollWatch );
this.bindScrollWatchEvents( true );
};
proto.disableScrollWatch = function() {
if ( !this.isScrollWatching ) return;
this.bindScrollWatchEvents( false );
delete this.isScrollWatching;
};
proto.bindScrollWatchEvents = function( isBind ) {
let addRemove = isBind ? 'addEventListener' : 'removeEventListener';
this.scroller[ addRemove ]( 'scroll', this.pageScrollHandler );
window[ addRemove ]( 'resize', this.resizeHandler );
};
proto.onPageScroll = InfiniteScroll.throttle( function() {
let distance = this.getBottomDistance();
if ( distance <= this.options.scrollThreshold ) {
this.dispatchEvent('scrollThreshold');
}
} );
proto.getBottomDistance = function() {
let bottom, scrollY;
if ( this.options.elementScroll ) {
bottom = this.scroller.scrollHeight;
scrollY = this.scroller.scrollTop + this.scroller.clientHeight;
} else {
bottom = this.top + this.element.clientHeight;
scrollY = window.scrollY + this.windowHeight;
}
return bottom - scrollY;
};
proto.onResize = function() {
this.updateMeasurements();
};
utils.debounceMethod( InfiniteScroll, 'onResize', 150 );
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );

View file

@ -1,102 +1,102 @@
// status
( 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;
// InfiniteScroll.defaults.status = null;
InfiniteScroll.create.status = function() {
let statusElem = utils.getQueryElement( this.options.status );
if ( !statusElem ) return;
// elements
this.statusElement = statusElem;
this.statusEventElements = {
request: statusElem.querySelector('.infinite-scroll-request'),
error: statusElem.querySelector('.infinite-scroll-error'),
last: statusElem.querySelector('.infinite-scroll-last'),
};
// events
this.on( 'request', this.showRequestStatus );
this.on( 'error', this.showErrorStatus );
this.on( 'last', this.showLastStatus );
this.bindHideStatus('on');
};
proto.bindHideStatus = function( bindMethod ) {
let hideEvent = this.options.append ? 'append' : 'load';
this[ bindMethod ]( hideEvent, this.hideAllStatus );
};
proto.showRequestStatus = function() {
this.showStatus('request');
};
proto.showErrorStatus = function() {
this.showStatus('error');
};
proto.showLastStatus = function() {
this.showStatus('last');
// prevent last then append event race condition from showing last status #706
this.bindHideStatus('off');
};
proto.showStatus = function( eventName ) {
show( this.statusElement );
this.hideStatusEventElements();
let eventElem = this.statusEventElements[ eventName ];
show( eventElem );
};
proto.hideAllStatus = function() {
hide( this.statusElement );
this.hideStatusEventElements();
};
proto.hideStatusEventElements = function() {
for ( let type in this.statusEventElements ) {
let eventElem = this.statusEventElements[ type ];
hide( eventElem );
}
};
// -------------------------- -------------------------- //
function hide( elem ) {
setDisplay( elem, 'none' );
}
function show( elem ) {
setDisplay( elem, 'block' );
}
function setDisplay( elem, value ) {
if ( elem ) {
elem.style.display = value;
}
}
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );
// status
( 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;
// InfiniteScroll.defaults.status = null;
InfiniteScroll.create.status = function() {
let statusElem = utils.getQueryElement( this.options.status );
if ( !statusElem ) return;
// elements
this.statusElement = statusElem;
this.statusEventElements = {
request: statusElem.querySelector('.infinite-scroll-request'),
error: statusElem.querySelector('.infinite-scroll-error'),
last: statusElem.querySelector('.infinite-scroll-last'),
};
// events
this.on( 'request', this.showRequestStatus );
this.on( 'error', this.showErrorStatus );
this.on( 'last', this.showLastStatus );
this.bindHideStatus('on');
};
proto.bindHideStatus = function( bindMethod ) {
let hideEvent = this.options.append ? 'append' : 'load';
this[ bindMethod ]( hideEvent, this.hideAllStatus );
};
proto.showRequestStatus = function() {
this.showStatus('request');
};
proto.showErrorStatus = function() {
this.showStatus('error');
};
proto.showLastStatus = function() {
this.showStatus('last');
// prevent last then append event race condition from showing last status #706
this.bindHideStatus('off');
};
proto.showStatus = function( eventName ) {
show( this.statusElement );
this.hideStatusEventElements();
let eventElem = this.statusEventElements[ eventName ];
show( eventElem );
};
proto.hideAllStatus = function() {
hide( this.statusElement );
this.hideStatusEventElements();
};
proto.hideStatusEventElements = function() {
for ( let type in this.statusEventElements ) {
let eventElem = this.statusEventElements[ type ];
hide( eventElem );
}
};
// -------------------------- -------------------------- //
function hide( elem ) {
setDisplay( elem, 'none' );
}
function show( elem ) {
setDisplay( elem, 'block' );
}
function setDisplay( elem, value ) {
if ( elem ) {
elem.style.display = value;
}
}
// -------------------------- -------------------------- //
return InfiniteScroll;
} ) );