Current File : /home/escuelai/public_html/web2021/wp-content/themes/oceanwp/assets/js/third/infinitescroll.js |
/*!
* Infinite Scroll PACKAGED v3.0.2
* Automatically add next page
*
* Licensed GPLv3 for open source use
* or Infinite Scroll Commercial License for commercial use
*
* https://infinite-scroll.com
* Copyright 2017 Metafizzy
*/
/**
* Bridget makes jQuery widgets
* v2.0.1
* MIT license
*/
/* jshint browser: true, strict: true, undef: true, unused: true */
( function( window, factory ) {
// universal module definition
/*jshint strict: false */ /* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
return factory( window, jQuery );
});
} else if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
window,
require('jquery')
);
} else {
// browser global
window.jQueryBridget = factory(
window,
window.jQuery
);
}
}( window, function factory( window, jQuery ) {
'use strict';
// ----- utils ----- //
var arraySlice = Array.prototype.slice;
// helper function for logging errors
// $.error breaks jQuery chaining
var console = window.console;
var logError = typeof console == 'undefined' ? function() {} :
function( message ) {
console.error( message );
};
// ----- jQueryBridget ----- //
function jQueryBridget( namespace, PluginClass, $ ) {
$ = $ || jQuery || window.jQuery;
if ( !$ ) {
return;
}
// add option method -> $().plugin('option', {...})
if ( !PluginClass.prototype.option ) {
// option setter
PluginClass.prototype.option = function( opts ) {
// bail out if not an object
if ( !$.isPlainObject( opts ) ){
return;
}
this.options = $.extend( true, this.options, opts );
};
}
// make jQuery plugin
$.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
if ( typeof arg0 == 'string' ) {
// method call $().plugin( 'methodName', { options } )
// shift arguments by 1
var args = arraySlice.call( arguments, 1 );
return methodCall( this, arg0, args );
}
// just $().plugin({ options })
plainCall( this, arg0 );
return this;
};
// $().plugin('methodName')
function methodCall( $elems, methodName, args ) {
var returnValue;
var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
$elems.each( function( i, elem ) {
// get instance
var instance = $.data( elem, namespace );
if ( !instance ) {
logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
pluginMethodStr );
return;
}
var method = instance[ methodName ];
if ( !method || methodName.charAt(0) == '_' ) {
logError( pluginMethodStr + ' is not a valid method' );
return;
}
// apply method, get return value
var value = method.apply( instance, args );
// set return value if value is returned, use only first value
returnValue = returnValue === undefined ? value : returnValue;
});
return returnValue !== undefined ? returnValue : $elems;
}
function plainCall( $elems, options ) {
$elems.each( function( i, elem ) {
var instance = $.data( elem, namespace );
if ( instance ) {
// set options & init
instance.option( options );
instance._init();
} else {
// initialize new instance
instance = new PluginClass( elem, options );
$.data( elem, namespace, instance );
}
});
}
updateJQuery( $ );
}
// ----- updateJQuery ----- //
// set $.bridget for v1 backwards compatibility
function updateJQuery( $ ) {
if ( !$ || ( $ && $.bridget ) ) {
return;
}
$.bridget = jQueryBridget;
}
updateJQuery( jQuery || window.jQuery );
// ----- ----- //
return jQueryBridget;
}));
/**
* EvEmitter v1.1.0
* Lil' event emitter
* MIT License
*/
/* jshint unused: true, undef: true, strict: true */
( function( global, factory ) {
// universal module definition
/* jshint strict: false */ /* globals define, module, window */
if ( typeof define == 'function' && define.amd ) {
// AMD - RequireJS
define( 'ev-emitter/ev-emitter',factory );
} else if ( typeof module == 'object' && module.exports ) {
// CommonJS - Browserify, Webpack
module.exports = factory();
} else {
// Browser globals
global.EvEmitter = factory();
}
}( typeof window != 'undefined' ? window : this, function() {
function EvEmitter() {}
var proto = EvEmitter.prototype;
proto.on = function( eventName, listener ) {
if ( !eventName || !listener ) {
return;
}
// set events hash
var events = this._events = this._events || {};
// set listeners array
var listeners = events[ eventName ] = events[ eventName ] || [];
// only add once
if ( listeners.indexOf( listener ) == -1 ) {
listeners.push( listener );
}
return this;
};
proto.once = function( eventName, listener ) {
if ( !eventName || !listener ) {
return;
}
// add event
this.on( eventName, listener );
// set once flag
// set onceEvents hash
var onceEvents = this._onceEvents = this._onceEvents || {};
// set onceListeners object
var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
// set flag
onceListeners[ listener ] = true;
return this;
};
proto.off = function( eventName, listener ) {
var listeners = this._events && this._events[ eventName ];
if ( !listeners || !listeners.length ) {
return;
}
var index = listeners.indexOf( listener );
if ( index != -1 ) {
listeners.splice( index, 1 );
}
return this;
};
proto.emitEvent = function( eventName, args ) {
var listeners = this._events && this._events[ eventName ];
if ( !listeners || !listeners.length ) {
return;
}
var i = 0;
var listener = listeners[i];
args = args || [];
// once stuff
var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
while ( listener ) {
var isOnce = onceListeners && onceListeners[ listener ];
if ( isOnce ) {
// remove listener
// remove before trigger to prevent recursion
this.off( eventName, listener );
// unset once flag
delete onceListeners[ listener ];
}
// trigger listener
listener.apply( this, args );
// get next listener
i += isOnce ? 0 : 1;
listener = listeners[i];
}
return this;
};
proto.allOff =
proto.removeAllListeners = function() {
delete this._events;
delete this._onceEvents;
};
return EvEmitter;
}));
/**
* matchesSelector v2.0.2
* matchesSelector( element, '.selector' )
* MIT license
*/
/*jshint browser: true, strict: true, undef: true, unused: true */
( function( window, factory ) {
/*global define: false, module: false */
'use strict';
// universal module definition
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'desandro-matches-selector/matches-selector',factory );
} else if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory();
} else {
// browser global
window.matchesSelector = factory();
}
}( window, function factory() {
'use strict';
var matchesMethod = ( function() {
var ElemProto = window.Element.prototype;
// check for the standard method name first
if ( ElemProto.matches ) {
return 'matches';
}
// check un-prefixed
if ( ElemProto.matchesSelector ) {
return 'matchesSelector';
}
// check vendor prefixes
var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
for ( var i=0; i < prefixes.length; i++ ) {
var prefix = prefixes[i];
var method = prefix + 'MatchesSelector';
if ( ElemProto[ method ] ) {
return method;
}
}
})();
return function matchesSelector( elem, selector ) {
return elem[ matchesMethod ]( selector );
};
}));
/**
* Fizzy UI utils v2.0.5
* MIT license
*/
/*jshint browser: true, undef: true, unused: true, strict: true */
( function( window, factory ) {
// universal module definition
/*jshint strict: false */ /*globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'fizzy-ui-utils/utils',[
'desandro-matches-selector/matches-selector'
], function( matchesSelector ) {
return factory( window, matchesSelector );
});
} else if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
window,
require('desandro-matches-selector')
);
} else {
// browser global
window.fizzyUIUtils = factory(
window,
window.matchesSelector
);
}
}( window, function factory( window, matchesSelector ) {
var utils = {};
// ----- extend ----- //
// extends objects
utils.extend = function( a, b ) {
for ( var prop in b ) {
a[ prop ] = b[ prop ];
}
return a;
};
// ----- modulo ----- //
utils.modulo = function( num, div ) {
return ( ( num % div ) + div ) % div;
};
// ----- makeArray ----- //
// turn element or nodeList into an array
utils.makeArray = function( obj ) {
var ary = [];
if ( Array.isArray( obj ) ) {
// use object if already an array
ary = obj;
} else if ( obj && typeof obj == 'object' &&
typeof obj.length == 'number' ) {
// convert nodeList to array
for ( var i=0; i < obj.length; i++ ) {
ary.push( obj[i] );
}
} else {
// array of single index
ary.push( obj );
}
return ary;
};
// ----- removeFrom ----- //
utils.removeFrom = function( ary, obj ) {
var index = ary.indexOf( obj );
if ( index != -1 ) {
ary.splice( index, 1 );
}
};
// ----- getParent ----- //
utils.getParent = function( elem, selector ) {
while ( elem.parentNode && elem != document.body ) {
elem = elem.parentNode;
if ( matchesSelector( elem, selector ) ) {
return elem;
}
}
};
// ----- getQueryElement ----- //
// use element as selector string
utils.getQueryElement = function( elem ) {
if ( typeof elem == 'string' ) {
return document.querySelector( elem );
}
return elem;
};
// ----- handleEvent ----- //
// enable .ontype to trigger from .addEventListener( elem, 'type' )
utils.handleEvent = function( event ) {
var method = 'on' + event.type;
if ( this[ method ] ) {
this[ method ]( event );
}
};
// ----- filterFindElements ----- //
utils.filterFindElements = function( elems, selector ) {
// make array of elems
elems = utils.makeArray( elems );
var ffElems = [];
elems.forEach( function( elem ) {
// check that elem is an actual element
if ( !( elem instanceof HTMLElement ) ) {
return;
}
// add elem if no selector
if ( !selector ) {
ffElems.push( elem );
return;
}
// filter & find items if we have a selector
// filter
if ( matchesSelector( elem, selector ) ) {
ffElems.push( elem );
}
// find children
var childElems = elem.querySelectorAll( selector );
// concat childElems to filterFound array
for ( var i=0; i < childElems.length; i++ ) {
ffElems.push( childElems[i] );
}
});
return ffElems;
};
// ----- debounceMethod ----- //
utils.debounceMethod = function( _class, methodName, threshold ) {
// original method
var method = _class.prototype[ methodName ];
var timeoutName = methodName + 'Timeout';
_class.prototype[ methodName ] = function() {
var timeout = this[ timeoutName ];
if ( timeout ) {
clearTimeout( timeout );
}
var args = arguments;
var _this = this;
this[ timeoutName ] = setTimeout( function() {
method.apply( _this, args );
delete _this[ timeoutName ];
}, threshold || 100 );
};
};
// ----- docReady ----- //
utils.docReady = function( callback ) {
var readyState = document.readyState;
if ( readyState == 'complete' || readyState == 'interactive' ) {
// do async to allow for other scripts to run. metafizzy/flickity#441
setTimeout( callback );
} else {
document.addEventListener( 'DOMContentLoaded', callback );
}
};
// ----- htmlInit ----- //
// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
utils.toDashed = function( str ) {
return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
return $1 + '-' + $2;
}).toLowerCase();
};
var console = window.console;
/**
* allow user to initialize classes via [data-namespace] or .js-namespace class
* htmlInit( Widget, 'widgetName' )
* options are parsed from data-namespace-options
*/
utils.htmlInit = function( WidgetClass, namespace ) {
utils.docReady( function() {
var dashedNamespace = utils.toDashed( namespace );
var dataAttr = 'data-' + dashedNamespace;
var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
var elems = utils.makeArray( dataAttrElems )
.concat( utils.makeArray( jsDashElems ) );
var dataOptionsAttr = dataAttr + '-options';
var jQuery = window.jQuery;
elems.forEach( function( elem ) {
var attr = elem.getAttribute( dataAttr ) ||
elem.getAttribute( dataOptionsAttr );
var options;
try {
options = attr && JSON.parse( attr );
} catch ( error ) {
// log error, do not initialize
if ( console ) {
console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
': ' + error );
}
return;
}
// initialize
var instance = new WidgetClass( elem, options );
// make available via $().data('namespace')
if ( jQuery ) {
jQuery.data( elem, namespace, instance );
}
});
});
};
// ----- ----- //
return utils;
}));
// core
( function( window, factory ) {
// universal module definition
/* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'infinite-scroll/js/core',[
'ev-emitter/ev-emitter',
'fizzy-ui-utils/utils',
], function( EvEmitter, utils) {
return factory( window, EvEmitter, utils );
});
} else 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 ) {
var jQuery = window.jQuery;
// internal store of all InfiniteScroll intances
var instances = {};
function InfiniteScroll( element, options ) {
var 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 ) {
var instance = instances[ element.infiniteScrollGUID ];
instance.option( options );
return instance;
}
this.element = element;
// options
this.options = utils.extend( {}, 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 = {};
var proto = InfiniteScroll.prototype;
// inherit EvEmitter
utils.extend( proto, EvEmitter.prototype );
// -------------------------- -------------------------- //
// globally unique identifiers
var GUID = 0;
proto.create = function() {
// create core
// add id for InfiniteScroll.data
var 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
if ( !this.getPath ) {
console.error('Disabling InfiniteScroll');
return;
}
this.updateGetAbsolutePath();
this.log( 'initialized', [ this.element.className ] );
this.callOnInit();
// create features
for ( var method in InfiniteScroll.create ) {
InfiniteScroll.create[ method ].call( this );
}
};
proto.option = function( opts ) {
utils.extend( this.options, opts );
};
// call onInit option, used for binding events on init
proto.callOnInit = function() {
var onInit = this.options.onInit;
if ( onInit ) {
onInit.call( this, this );
}
};
// ----- events ----- //
proto.dispatchEvent = function( type, event, args ) {
this.log( type, args );
var emitArgs = event ? [ event ].concat( args ) : args;
this.emitEvent( type, emitArgs );
// trigger jQuery event
if ( !jQuery || !this.$element ) {
return;
}
// namespace jQuery event
type += '.infiniteScroll';
var $event = type;
if ( event ) {
// create jQuery event
var jQEvent = jQuery.Event( event );
jQEvent.type = type;
$event = jQEvent;
}
this.$element.trigger( $event, args );
};
var loggers = {
initialized: function( className ) {
return 'on ' + className;
},
request: function( path ) {
return 'URL: ' + path;
},
load: function( response, path ) {
return ( response.title || '' ) + '. URL: ' + path;
},
error: function( error, path ) {
return error + '. URL: ' + path;
},
append: function( response, path, items ) {
return items.length + ' items. URL: ' + path;
},
last: function( response, path ) {
return 'URL: ' + path;
},
history: function( title, path ) {
return '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;
}
var message = '[InfiniteScroll] ' + type;
var logger = loggers[ type ];
if ( logger ) {
message += '. ' + logger.apply( this, args );
}
console.log( message );
};
// -------------------------- methods used amoung features -------------------------- //
proto.updateMeasurements = function() {
this.windowHeight = window.innerHeight;
var rect = this.element.getBoundingClientRect();
this.top = rect.top + window.pageYOffset;
};
proto.updateScroller = function() {
var 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 'Unable to find elementScroll: ' + elementScroll;
}
};
// -------------------------- page path -------------------------- //
proto.updateGetPath = function() {
var optPath = this.options.path;
if ( !optPath ) {
console.error( 'InfiniteScroll path option required. Set as: ' + optPath );
return;
}
// function
var type = typeof optPath;
if ( type == 'function' ) {
this.getPath = optPath;
return;
}
// template string: '/pages/{{#}}.html'
var 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 = function() {
var nextIndex = this.pageIndex + 1;
return optPath.replace( '{{#}}', nextIndex );
}.bind( this );
// get pageIndex from location
// convert path option into regex to look for pattern in location
var regexString = optPath.replace( '{{#}}', '(\\d\\d?\\d?)' );
var templateRe = new RegExp( regexString );
var match = location.href.match( templateRe );
if ( match ) {
this.pageIndex = parseInt( match[1], 10 );
this.log( 'pageIndex', this.pageIndex, 'template string' );
}
};
var 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)(.*?$)/,
];
proto.updateGetPathSelector = function( optPath ) {
// parse href of link: '.next-page-link'
var hrefElem = document.querySelector( optPath );
if ( !hrefElem ) {
console.error( 'Bad InfiniteScroll path option. Next link not found: ' +
optPath );
return;
}
var href = hrefElem.getAttribute('href');
// try matching href to pathRegexes patterns
var pathParts, regex;
for ( var i=0; href && i < pathRegexes.length; i++ ) {
regex = pathRegexes[i];
var match = href.match( regex );
if ( match ) {
pathParts = match.slice(1); // remove first part
break;
}
}
if ( !pathParts ) {
console.error( 'InfiniteScroll unable to parse next link href: ' + href );
return;
}
this.isPathSelector = true; // flag for checkLastPage()
this.getPath = function() {
var nextIndex = this.pageIndex + 1;
return pathParts[0] + nextIndex + pathParts[2];
}.bind( this );
// get pageIndex from href
this.pageIndex = parseInt( pathParts[1], 10 ) - 1;
this.log( 'pageIndex', [ this.pageIndex, 'next link' ] );
};
proto.updateGetAbsolutePath = function() {
var path = this.getPath();
// path doesn't start with http or /
var isAbsolute = path.match( /^http/ ) || path.match( /^\// );
if ( isAbsolute ) {
this.getAbsolutePath = this.getPath;
return;
}
var pathname = location.pathname;
// /foo/bar/index.html => /foo/bar
var directory = pathname.substring( 0, pathname.lastIndexOf('/') );
this.getAbsolutePath = function() {
return directory + '/' + this.getPath();
};
};
// -------------------------- nav -------------------------- //
// hide navigation
InfiniteScroll.create.hideNav = function() {
var 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 ( var method in InfiniteScroll.destroy ) {
InfiniteScroll.destroy[ method ].call( this );
}
delete this.element.infiniteScrollGUID;
delete instances[ this.guid ];
};
// -------------------------- utilities -------------------------- //
// https://remysharp.com/2010/07/21/throttling-function-calls
InfiniteScroll.throttle = function( fn, threshold ) {
threshold = threshold || 200;
var last, timeout;
return function() {
var now = +new Date();
var args = arguments;
var trigger = function() {
last = now;
fn.apply( this, args );
}.bind( this );
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 );
var id = elem && elem.infiniteScrollGUID;
return id && instances[ id ];
};
// set internal jQuery, for Webpack + jQuery v3
InfiniteScroll.setJQuery = function( $ ) {
jQuery = $;
};
// -------------------------- setup -------------------------- //
utils.htmlInit( InfiniteScroll, 'infinite-scroll' );
if ( jQuery && jQuery.bridget ) {
jQuery.bridget( 'infiniteScroll', InfiniteScroll );
}
// -------------------------- -------------------------- //
return InfiniteScroll;
}));
// page-load
( function( window, factory ) {
// universal module definition
/* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'infinite-scroll/js/page-load',[
'./core',
], function( InfiniteScroll ) {
return factory( window, InfiniteScroll );
});
} else 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 ) {
var proto = InfiniteScroll.prototype;
// InfiniteScroll.defaults.append = false;
InfiniteScroll.defaults.loadOnScroll = true;
InfiniteScroll.defaults.checkLastPage = true;
InfiniteScroll.defaults.responseType = 'document';
// InfiniteScroll.defaults.prefill = false;
// InfiniteScroll.defaults.outlayer = null;
InfiniteScroll.create.pageLoad = function() {
this.canLoad = true;
this.on( 'scrollThreshold', this.onScrollThresholdLoad );
this.on( 'append', this.checkLastPage );
if ( this.options.outlayer ) {
this.on( 'append', this.onAppendOutlayer );
}
};
proto.onScrollThresholdLoad = function() {
if ( this.options.loadOnScroll ) {
this.loadNextPage();
}
};
proto.loadNextPage = function() {
if ( this.isLoading || !this.canLoad ) {
return;
}
var path = this.getAbsolutePath();
this.isLoading = true;
var onLoad = function( response ) {
this.onPageLoad( response, path );
}.bind( this );
var onError = function( error ) {
this.onPageError( error, path );
}.bind( this );
request( path, this.options.responseType, onLoad, onError );
this.dispatchEvent( 'request', null, [ path ] );
};
proto.onPageLoad = function( response, path ) {
// done loading if not appending
if ( !this.options.append ) {
this.isLoading = false;
}
this.pageIndex++;
this.loadCount++;
this.dispatchEvent( 'load', null, [ response, path ] );
this.appendNextPage( response, path );
return response;
};
proto.appendNextPage = function( response, path ) {
var optAppend = this.options.append;
// do not append json
var isDocument = this.options.responseType == 'document';
if ( !isDocument || !optAppend ) {
return;
}
var items = response.querySelectorAll( optAppend );
var fragment = getItemsFragment( items );
var appendReady = function () {
this.appendItems( items, fragment );
this.isLoading = false;
this.dispatchEvent( 'append', null, [ response, path, items ] );
}.bind( this );
// TODO add hook for option to trigger appendReady
if ( this.options.outlayer ) {
this.appendOutlayerItems( fragment, appendReady );
} else {
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
var fragment = document.createDocumentFragment();
for ( var i=0; items && i < items.length; i++ ) {
fragment.appendChild( items[i] );
}
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 ) {
var scripts = fragment.querySelectorAll('script');
for ( var i=0; i < scripts.length; i++ ) {
var script = scripts[i];
var freshScript = document.createElement('script');
copyAttributes( script, freshScript );
script.parentNode.replaceChild( freshScript, script );
}
}
function copyAttributes( fromNode, toNode ) {
var attrs = fromNode.attributes;
for ( var i=0; i < attrs.length; i++ ) {
var attr = attrs[i];
toNode.setAttribute( attr.name, attr.value );
}
}
// ----- outlayer ----- //
proto.appendOutlayerItems = function( fragment, appendReady ) {
var imagesLoaded = InfiniteScroll.imagesLoaded || window.imagesLoaded;
if ( !imagesLoaded ) {
console.error('[InfiniteScroll] imagesLoaded required for outlayer option');
this.isLoading = false;
return;
}
// append once images loaded
imagesLoaded( fragment, appendReady );
};
proto.onAppendOutlayer = function( response, path, items ) {
this.options.outlayer.appended( items );
};
// ----- checkLastPage ----- //
// check response for next element
proto.checkLastPage = function( response, path ) {
var checkLastPage = this.options.checkLastPage;
if ( !checkLastPage ) {
return;
}
var pathOpt = this.options.path;
// if path is function, check if next path is truthy
if ( typeof pathOpt == 'function' ) {
var nextPath = this.getPath();
if ( !nextPath ) {
this.lastPageReached( response, path );
return;
}
}
// get selector from checkLastPage or path option
var 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 || !response.querySelector ) {
return;
}
// check if response has selector
var nextElem = response.querySelector( selector );
if ( !nextElem ) {
this.lastPageReached( response, path );
}
};
proto.lastPageReached = function( response, path ) {
this.canLoad = false;
this.dispatchEvent( 'last', null, [ response, path ] );
};
// ----- error ----- //
proto.onPageError = function( error, path ) {
this.isLoading = false;
this.canLoad = false;
this.dispatchEvent( 'error', null, [ error, path ] );
return error;
};
// -------------------------- prefill -------------------------- //
InfiniteScroll.create.prefill = function() {
if ( !this.options.prefill ) {
return;
}
var 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() {
var 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() {
console.log('stopping prefill');
this.off( 'append', this.prefill );
};
// -------------------------- request -------------------------- //
function request( url, responseType, onLoad, onError ) {
var req = new XMLHttpRequest();
req.open( 'GET', url, true );
// set responseType document to return DOM
req.responseType = responseType || '';
// set X-Requested-With header to check that is ajax request
req.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
req.onload = function() {
if ( req.status == 200 ) {
onLoad( req.response );
} else {
// not 200 OK, error
var error = new Error( req.statusText );
onError( error );
}
};
// Handle network errors
req.onerror = function() {
var error = new Error( 'Network error requesting ' + url );
onError( error );
};
req.send();
}
// -------------------------- -------------------------- //
return InfiniteScroll;
}));
// scroll-watch
( function( window, factory ) {
// universal module definition
/* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'infinite-scroll/js/scroll-watch',[
'./core',
'fizzy-ui-utils/utils',
], function( InfiniteScroll, utils ) {
return factory( window, InfiniteScroll, utils );
});
} else 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 ) {
var proto = InfiniteScroll.prototype;
// default options
InfiniteScroll.defaults.scrollThreshold = 400;
// InfiniteScroll.defaults.elementScroll = null;
InfiniteScroll.create.scrollWatch = function() {
// events
this.pageScrollHandler = this.onPageScroll.bind( this );
this.resizeHandler = this.onResize.bind( this );
var scrollThreshold = this.options.scrollThreshold;
var 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 ) {
var addRemove = isBind ? 'addEventListener' : 'removeEventListener';
this.scroller[ addRemove ]( 'scroll', this.pageScrollHandler );
window[ addRemove ]( 'resize', this.resizeHandler );
};
proto.onPageScroll = InfiniteScroll.throttle( function() {
var distance = this.getBottomDistance();
if ( distance <= this.options.scrollThreshold ) {
this.dispatchEvent('scrollThreshold');
}
});
proto.getBottomDistance = function() {
if ( this.options.elementScroll ) {
return this.getElementBottomDistance();
} else {
return this.getWindowBottomDistance();
}
};
proto.getWindowBottomDistance = function() {
var bottom = this.top + this.element.clientHeight;
var scrollY = window.pageYOffset + this.windowHeight;
return bottom - scrollY;
};
proto.getElementBottomDistance = function() {
var bottom = this.scroller.scrollHeight;
var scrollY = this.scroller.scrollTop + this.scroller.clientHeight;
return bottom - scrollY;
};
proto.onResize = function() {
this.updateMeasurements();
};
utils.debounceMethod( InfiniteScroll, 'onResize', 150 );
// -------------------------- -------------------------- //
return InfiniteScroll;
}));
// history
( function( window, factory ) {
// universal module definition
/* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'infinite-scroll/js/history',[
'./core',
'fizzy-ui-utils/utils',
], function( InfiniteScroll, utils ) {
return factory( window, InfiniteScroll, utils );
});
} else 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 ) {
var proto = InfiniteScroll.prototype;
InfiniteScroll.defaults.history = 'replace';
// InfiniteScroll.defaults.historyTitle = false;
var 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/
var linkOrigin = link.origin || link.protocol + '//' + link.host;
var 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.scrollPageIndex = 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 ) {
var 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() {
var isHistoryAppend = this.options.history && this.options.append;
if ( isHistoryAppend ) {
this.bindHistoryAppendEvents( false );
}
};
// ----- append history ----- //
proto.onAppendHistory = function( response, path, items ) {
var firstItem = items[0];
var 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 this.getElementElementScrollY( elem );
} else {
return this.getElementWindowScrollY( elem );
}
};
proto.getElementWindowScrollY = function( elem ) {
var rect = elem.getBoundingClientRect();
return rect.top + window.pageYOffset;
};
// wow, stupid name
proto.getElementElementScrollY = function( elem ) {
return elem.offsetTop - this.top;
};
proto.onScrollHistory = function() {
// cycle through positions, find biggest without going over
var scrollViewY = this.getScrollViewY();
var pageIndex, page;
for ( var i=0; i < this.scrollPages.length; i++ ) {
var scrollPage = this.scrollPages[i];
if ( scrollPage.top >= scrollViewY ) {
break;
}
pageIndex = i;
page = scrollPage;
}
// set history if changed
if ( pageIndex != this.scrollPageIndex ) {
this.scrollPageIndex = pageIndex;
this.setHistory( page.title, page.path );
}
};
utils.debounceMethod( InfiniteScroll, 'onScrollHistory', 150 );
proto.getScrollViewY = function() {
if ( this.options.elementScroll ) {
return this.scroller.scrollTop + this.scroller.clientHeight/2;
} else {
return window.pageYOffset + this.windowHeight/2;
}
};
proto.setHistory = function( title, path ) {
var optHistory = this.options.history;
var 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
// http://stackoverflow.com/a/18633915/182183
proto.onUnload = function() {
var pageIndex = this.scrollPageIndex;
if ( pageIndex === 0 ) {
return;
}
// calculate where scroll position would be on refresh
var scrollPage = this.scrollPages[ pageIndex ];
var scrollY = window.pageYOffset - 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;
}));
// button
( function( window, factory ) {
// universal module definition
/* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'infinite-scroll/js/button',[
'./core',
'fizzy-ui-utils/utils',
], function( InfiniteScroll, utils ) {
return factory( window, InfiniteScroll, utils );
});
} else 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 ) {
// InfiniteScroll.defaults.button = null;
InfiniteScroll.create.button = function() {
var buttonElem = utils.getQueryElement( this.options.button );
if ( buttonElem ) {
this.button = new InfiniteScrollButton( buttonElem, this );
return;
}
};
InfiniteScroll.destroy.button = function() {
if ( this.button ) {
this.button.destroy();
}
};
// -------------------------- InfiniteScrollButton -------------------------- //
function InfiniteScrollButton( 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 ) );
}
InfiniteScrollButton.prototype.onClick = function( event ) {
event.preventDefault();
this.infScroll.loadNextPage();
};
InfiniteScrollButton.prototype.enable = function() {
this.element.removeAttribute('disabled');
};
InfiniteScrollButton.prototype.disable = function() {
this.element.disabled = 'disabled';
};
InfiniteScrollButton.prototype.hide = function() {
this.element.style.display = 'none';
};
InfiniteScrollButton.prototype.destroy = function() {
this.element.removeEventListener( 'click', this.clickHandler );
};
// -------------------------- -------------------------- //
InfiniteScroll.Button = InfiniteScrollButton;
return InfiniteScroll;
}));
// status
( function( window, factory ) {
// universal module definition
/* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'infinite-scroll/js/status',[
'./core',
'fizzy-ui-utils/utils',
], function( InfiniteScroll, utils ) {
return factory( window, InfiniteScroll, utils );
});
} else 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 ) {
var proto = InfiniteScroll.prototype;
// InfiniteScroll.defaults.status = null;
InfiniteScroll.create.status = function() {
var 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 ) {
var 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();
var eventElem = this.statusEventElements[ eventName ];
show( eventElem );
};
proto.hideAllStatus = function() {
hide( this.statusElement );
this.hideStatusEventElements();
};
proto.hideStatusEventElements = function() {
for ( var type in this.statusEventElements ) {
var 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;
}));
/*!
* Infinite Scroll v3.0.2
* Automatically add next page
*
* Licensed GPLv3 for open source use
* or Infinite Scroll Commercial License for commercial use
*
* https://infinite-scroll.com
* Copyright 2017 Metafizzy
*/
( function( window, factory ) {
// universal module definition
/* globals define, module, require */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( [
'infinite-scroll/js/core',
'infinite-scroll/js/page-load',
'infinite-scroll/js/scroll-watch',
'infinite-scroll/js/history',
'infinite-scroll/js/button',
'infinite-scroll/js/status',
], factory );
} else 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;
});
/*!
* imagesLoaded v4.1.3
* JavaScript is all like "You images are done yet or what?"
* MIT License
*/
( function( window, factory ) { 'use strict';
// universal module definition
/*global define: false, module: false, require: false */
if ( typeof define == 'function' && define.amd ) {
// AMD
define( 'imagesloaded/imagesloaded',[
'ev-emitter/ev-emitter'
], function( EvEmitter ) {
return factory( window, EvEmitter );
});
} else if ( typeof module == 'object' && module.exports ) {
// CommonJS
module.exports = factory(
window,
require('ev-emitter')
);
} else {
// browser global
window.imagesLoaded = factory(
window,
window.EvEmitter
);
}
})( typeof window !== 'undefined' ? window : this,
// -------------------------- factory -------------------------- //
function factory( window, EvEmitter ) {
var $ = window.jQuery;
var console = window.console;
// -------------------------- helpers -------------------------- //
// extend objects
function extend( a, b ) {
for ( var prop in b ) {
a[ prop ] = b[ prop ];
}
return a;
}
// turn element or nodeList into an array
function makeArray( obj ) {
var ary = [];
if ( Array.isArray( obj ) ) {
// use object if already an array
ary = obj;
} else if ( typeof obj.length == 'number' ) {
// convert nodeList to array
for ( var i=0; i < obj.length; i++ ) {
ary.push( obj[i] );
}
} else {
// array of single index
ary.push( obj );
}
return ary;
}
// -------------------------- imagesLoaded -------------------------- //
/**
* @param {Array, Element, NodeList, String} elem
* @param {Object or Function} options - if function, use as callback
* @param {Function} onAlways - callback function
*/
function ImagesLoaded( elem, options, onAlways ) {
// coerce ImagesLoaded() without new, to be new ImagesLoaded()
if ( !( this instanceof ImagesLoaded ) ) {
return new ImagesLoaded( elem, options, onAlways );
}
// use elem as selector string
if ( typeof elem == 'string' ) {
elem = document.querySelectorAll( elem );
}
this.elements = makeArray( elem );
this.options = extend( {}, this.options );
if ( typeof options == 'function' ) {
onAlways = options;
} else {
extend( this.options, options );
}
if ( onAlways ) {
this.on( 'always', onAlways );
}
this.getImages();
if ( $ ) {
// add jQuery Deferred object
this.jqDeferred = new $.Deferred();
}
// HACK check async to allow time to bind listeners
setTimeout( function() {
this.check();
}.bind( this ));
}
ImagesLoaded.prototype = Object.create( EvEmitter.prototype );
ImagesLoaded.prototype.options = {};
ImagesLoaded.prototype.getImages = function() {
this.images = [];
// filter & find items if we have an item selector
this.elements.forEach( this.addElementImages, this );
};
/**
* @param {Node} element
*/
ImagesLoaded.prototype.addElementImages = function( elem ) {
// filter siblings
if ( elem.nodeName == 'IMG' ) {
this.addImage( elem );
}
// get background image on element
if ( this.options.background === true ) {
this.addElementBackgroundImages( elem );
}
// find children
// no non-element nodes, #143
var nodeType = elem.nodeType;
if ( !nodeType || !elementNodeTypes[ nodeType ] ) {
return;
}
var childImgs = elem.querySelectorAll('img');
// concat childElems to filterFound array
for ( var i=0; i < childImgs.length; i++ ) {
var img = childImgs[i];
this.addImage( img );
}
// get child background images
if ( typeof this.options.background == 'string' ) {
var children = elem.querySelectorAll( this.options.background );
for ( i=0; i < children.length; i++ ) {
var child = children[i];
this.addElementBackgroundImages( child );
}
}
};
var elementNodeTypes = {
1: true,
9: true,
11: true
};
ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
var style = getComputedStyle( elem );
if ( !style ) {
// Firefox returns null if in a hidden iframe https://bugzil.la/548397
return;
}
// get url inside url("...")
var reURL = /url\((['"])?(.*?)\1\)/gi;
var matches = reURL.exec( style.backgroundImage );
while ( matches !== null ) {
var url = matches && matches[2];
if ( url ) {
this.addBackground( url, elem );
}
matches = reURL.exec( style.backgroundImage );
}
};
/**
* @param {Image} img
*/
ImagesLoaded.prototype.addImage = function( img ) {
var loadingImage = new LoadingImage( img );
this.images.push( loadingImage );
};
ImagesLoaded.prototype.addBackground = function( url, elem ) {
var background = new Background( url, elem );
this.images.push( background );
};
ImagesLoaded.prototype.check = function() {
var _this = this;
this.progressedCount = 0;
this.hasAnyBroken = false;
// complete if no images
if ( !this.images.length ) {
this.complete();
return;
}
function onProgress( image, elem, message ) {
// HACK - Chrome triggers event before object properties have changed. #83
setTimeout( function() {
_this.progress( image, elem, message );
});
}
this.images.forEach( function( loadingImage ) {
loadingImage.once( 'progress', onProgress );
loadingImage.check();
});
};
ImagesLoaded.prototype.progress = function( image, elem, message ) {
this.progressedCount++;
this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
// progress event
this.emitEvent( 'progress', [ this, image, elem ] );
if ( this.jqDeferred && this.jqDeferred.notify ) {
this.jqDeferred.notify( this, image );
}
// check if completed
if ( this.progressedCount == this.images.length ) {
this.complete();
}
if ( this.options.debug && console ) {
console.log( 'progress: ' + message, image, elem );
}
};
ImagesLoaded.prototype.complete = function() {
var eventName = this.hasAnyBroken ? 'fail' : 'done';
this.isComplete = true;
this.emitEvent( eventName, [ this ] );
this.emitEvent( 'always', [ this ] );
if ( this.jqDeferred ) {
var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';
this.jqDeferred[ jqMethod ]( this );
}
};
// -------------------------- -------------------------- //
function LoadingImage( img ) {
this.img = img;
}
LoadingImage.prototype = Object.create( EvEmitter.prototype );
LoadingImage.prototype.check = function() {
// If complete is true and browser supports natural sizes,
// try to check for image status manually.
var isComplete = this.getIsImageComplete();
if ( isComplete ) {
// report based on naturalWidth
this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
return;
}
// If none of the checks above matched, simulate loading on detached element.
this.proxyImage = new Image();
this.proxyImage.addEventListener( 'load', this );
this.proxyImage.addEventListener( 'error', this );
// bind to image as well for Firefox. #191
this.img.addEventListener( 'load', this );
this.img.addEventListener( 'error', this );
this.proxyImage.src = this.img.src;
};
LoadingImage.prototype.getIsImageComplete = function() {
return this.img.complete && this.img.naturalWidth !== undefined;
};
LoadingImage.prototype.confirm = function( isLoaded, message ) {
this.isLoaded = isLoaded;
this.emitEvent( 'progress', [ this, this.img, message ] );
};
// ----- events ----- //
// trigger specified handler for event type
LoadingImage.prototype.handleEvent = function( event ) {
var method = 'on' + event.type;
if ( this[ method ] ) {
this[ method ]( event );
}
};
LoadingImage.prototype.onload = function() {
this.confirm( true, 'onload' );
this.unbindEvents();
};
LoadingImage.prototype.onerror = function() {
this.confirm( false, 'onerror' );
this.unbindEvents();
};
LoadingImage.prototype.unbindEvents = function() {
this.proxyImage.removeEventListener( 'load', this );
this.proxyImage.removeEventListener( 'error', this );
this.img.removeEventListener( 'load', this );
this.img.removeEventListener( 'error', this );
};
// -------------------------- Background -------------------------- //
function Background( url, element ) {
this.url = url;
this.element = element;
this.img = new Image();
}
// inherit LoadingImage prototype
Background.prototype = Object.create( LoadingImage.prototype );
Background.prototype.check = function() {
this.img.addEventListener( 'load', this );
this.img.addEventListener( 'error', this );
this.img.src = this.url;
// check if image is already complete
var isComplete = this.getIsImageComplete();
if ( isComplete ) {
this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
this.unbindEvents();
}
};
Background.prototype.unbindEvents = function() {
this.img.removeEventListener( 'load', this );
this.img.removeEventListener( 'error', this );
};
Background.prototype.confirm = function( isLoaded, message ) {
this.isLoaded = isLoaded;
this.emitEvent( 'progress', [ this, this.element, message ] );
};
// -------------------------- jQuery -------------------------- //
ImagesLoaded.makeJQueryPlugin = function( jQuery ) {
jQuery = jQuery || window.jQuery;
if ( !jQuery ) {
return;
}
// set local variable
$ = jQuery;
// $().imagesLoaded()
$.fn.imagesLoaded = function( options, callback ) {
var instance = new ImagesLoaded( this, options, callback );
return instance.jqDeferred.promise( $(this) );
};
};
// try making plugin
ImagesLoaded.makeJQueryPlugin();
// -------------------------- -------------------------- //
return ImagesLoaded;
});