PageSourceSearch

https://compassnews360.com/wp-content/plugins/responsive-lightbox/…/infinite-scroll.pkgd.js?ver=4.0.1

js compassnews360.com collected 2026-10-02 13:45:27 UTC 50,026 bytes, 1,906 lines download raw bytes

vendor: 50,026 bytes, lines 1-1906
1/*!
2 * Infinite Scroll PACKAGED v5.0.0
3 * Automatically add next page
4 * MIT License
5 * https://infinite-scroll.com
6 * Copyright 2018-2025 Metafizzy
7 */
8
9/**
10 * Bridget makes jQuery widgets
11 * v3.0.0
12 * MIT license
13 */
14
15( function( window, factory ) {
16  // module definition
17 if ( typeof module == 'object' && module.exports ) {
18   // CommonJS
19   module.exports = factory(
20       window,
21       require('jquery'),
22   );
23 } else {
24   // browser global
25   window.jQueryBridget = factory(
26       window,
27       window.jQuery,
28   );
29 }
30
31}( window, function factory( window, jQuery ) {
32
33// ----- utils ----- //
34
35// helper function for logging errors
36// $.error breaks jQuery chaining
37let console = window.console;
38let logError = typeof console == 'undefined' ? function() {} :
39  function( message ) {
40    console.error( message );
41  };
42
43// ----- jQueryBridget ----- //
44
45function jQueryBridget( namespace, PluginClass, $ ) {
46  $ = $ || jQuery || window.jQuery;
47  if ( !$ ) {
48    return;
49  }
50
51  // add option method -> $().plugin('option', {...})
52  if ( !PluginClass.prototype.option ) {
53    // option setter
54    PluginClass.prototype.option = function( opts ) {
55      if ( !opts ) return;
56
57      this.options = Object.assign( this.options || {}, opts );
58    };
59  }
60
61  // make jQuery plugin
62  $.fn[ namespace ] = function( arg0, ...args ) {
63    if ( typeof arg0 == 'string' ) {
64      // method call $().plugin( 'methodName', { options } )
65      return methodCall( this, arg0, args );
66    }
67    // just $().plugin({ options })
68    plainCall( this, arg0 );
69    return this;
70  };
71
72  // $().plugin('methodName')
73  function methodCall( $elems, methodName, args ) {
74    let returnValue;
75    let pluginMethodStr = `$().${namespace}("${methodName}")`;
76
77    $elems.each( function( i, elem ) {
78      // get instance
79      let instance = $.data( elem, namespace );
80      if ( !instance ) {
81        logError( `${namespace} not initialized.` +
82          ` Cannot call method ${pluginMethodStr}` );
83        return;
84      }
85
86      let method = instance[ methodName ];
87      if ( !method || methodName.charAt( 0 ) == '_' ) {
88        logError(`${pluginMethodStr} is not a valid method`);
89        return;
90      }
91
92      // apply method, get return value
93      let value = method.apply( instance, args );
94      // set return value if value is returned, use only first value
95      returnValue = returnValue === undefined ? value : returnValue;
96    } );
97
98    return returnValue !== undefined ? returnValue : $elems;
99  }
100
101  function plainCall( $elems, options ) {
102    $elems.each( function( i, elem ) {
103      let instance = $.data( elem, namespace );
104      if ( instance ) {
105        // set options & init
106        instance.option( options );
107        instance._init();
108      } else {
109        // initialize new instance
110        instance = new PluginClass( elem, options );
111        $.data( elem, namespace, instance );
112      }
113    } );
114  }
115
116}
117
118// -----  ----- //
119
120return jQueryBridget;
121
122} ) );
123/**
124 * EvEmitter v2.0.0
125 * Lil' event emitter
126 * MIT License
127 */
128
129( function( global, factory ) {
130  // universal module definition
131  if ( typeof module == 'object' && module.exports ) {
132    // CommonJS - Browserify, Webpack
133    module.exports = factory();
134  } else {
135    // Browser globals
136    global.EvEmitter = factory();
137  }
138
139}( typeof window != 'undefined' ? window : this, function() {
140
141function EvEmitter() {}
142
143let proto = EvEmitter.prototype;
144
145proto.on = function( eventName, listener ) {
146  if ( !eventName || !listener ) return this;
147
148  // set events hash
149  let events = this._events = this._events || {};
150  // set listeners array
151  let listeners = events[ eventName ] = events[ eventName ] || [];
152  // only add once
153  if ( !listeners.includes( listener ) ) {
154    listeners.push( listener );
155  }
156
157  return this;
158};
159
160proto.once = function( eventName, listener ) {
161  if ( !eventName || !listener ) return this;
162
163  // add event
164  this.on( eventName, listener );
165  // set once flag
166  // set onceEvents hash
167  let onceEvents = this._onceEvents = this._onceEvents || {};
168  // set onceListeners object
169  let onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
170  // set flag
171  onceListeners[ listener ] = true;
172
173  return this;
174};
175
176proto.off = function( eventName, listener ) {
177  let listeners = this._events && this._events[ eventName ];
178  if ( !listeners || !listeners.length ) return this;
179
180  let index = listeners.indexOf( listener );
181  if ( index != -1 ) {
182    listeners.splice( index, 1 );
183  }
184
185  return this;
186};
187
188proto.emitEvent = function( eventName, args ) {
189  let listeners = this._events && this._events[ eventName ];
190  if ( !listeners || !listeners.length ) return this;
191
192  // copy over to avoid interference if .off() in listener
193  listeners = listeners.slice( 0 );
194  args = args || [];
195  // once stuff
196  let onceListeners = this._onceEvents && this._onceEvents[ eventName ];
197
198  for ( let listener of listeners ) {
199    let isOnce = onceListeners && onceListeners[ listener ];
200    if ( isOnce ) {
201      // remove listener
202      // remove before trigger to prevent recursion
203      this.off( eventName, listener );
204      // unset once flag
205      delete onceListeners[ listener ];
206    }
207    // trigger listener
208    listener.apply( this, args );
209  }
210
211  return this;
212};
213
214proto.allOff = function() {
215  delete this._events;
216  delete this._onceEvents;
217  return this;
218};
219
220return EvEmitter;
221
222} ) );
223/**
224 * Fizzy UI utils v3.0.0
225 * MIT license
226 */
227
228( function( global, factory ) {
229  // universal module definition
230  if ( typeof module == 'object' && module.exports ) {
231    // CommonJS
232    module.exports = factory( global );
233  } else {
234    // browser global
235    global.fizzyUIUtils = factory( global );
236  }
237
238}( this, function factory( global ) {
239
240let utils = {};
241
242// ----- extend ----- //
243
244// extends objects
245utils.extend = function( a, b ) {
246  return Object.assign( a, b );
247};
248
249// ----- modulo ----- //
250
251utils.modulo = function( num, div ) {
252  return ( ( num % div ) + div ) % div;
253};
254
255// ----- makeArray ----- //
256
257// turn element or nodeList into an array
258utils.makeArray = function( obj ) {
259  // use object if already an array
260  if ( Array.isArray( obj ) ) return obj;
261
262  // return empty array if undefined or null. #6
263  if ( obj === null || obj === undefined ) return [];
264
265  let isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
266  // convert nodeList to array
267  if ( isArrayLike ) return [ ...obj ];
268
269  // array of single index
270  return [ obj ];
271};
272
273// ----- removeFrom ----- //
274
275utils.removeFrom = function( ary, obj ) {
276  let index = ary.indexOf( obj );
277  if ( index != -1 ) {
278    ary.splice( index, 1 );
279  }
280};
281
282// ----- getParent ----- //
283
284utils.getParent = function( elem, selector ) {
285  while ( elem.parentNode && elem != document.body ) {
286    elem = elem.parentNode;
287    if ( elem.matches( selector ) ) return elem;
288  }
289};
290
291// ----- getQueryElement ----- //
292
293// use element as selector string
294utils.getQueryElement = function( elem ) {
295  if ( typeof elem == 'string' ) {
296    return document.querySelector( elem );
297  }
298  return elem;
299};
300
301// ----- handleEvent ----- //
302
303// enable .ontype to trigger from .addEventListener( elem, 'type' )
304utils.handleEvent = function( event ) {
305  let method = 'on' + event.type;
306  if ( this[ method ] ) {
307    this[ method ]( event );
308  }
309};
310
311// ----- filterFindElements ----- //
312
313utils.filterFindElements = function( elems, selector ) {
314  // make array of elems
315  elems = utils.makeArray( elems );
316
317  return elems
318    // check that elem is an actual element
319    .filter( ( elem ) => elem instanceof HTMLElement )
320    .reduce( ( ffElems, elem ) => {
321      // add elem if no selector
322      if ( !selector ) {
323        ffElems.push( elem );
324        return ffElems;
325      }
326      // filter & find items if we have a selector
327      // filter
328      if ( elem.matches( selector ) ) {
329        ffElems.push( elem );
330      }
331      // find children
332      let childElems = elem.querySelectorAll( selector );
333      // concat childElems to filterFound array
334      ffElems = ffElems.concat( ...childElems );
335      return ffElems;
336    }, [] );
337};
338
339// ----- debounceMethod ----- //
340
341utils.debounceMethod = function( _class, methodName, threshold ) {
342  threshold = threshold || 100;
343  // original method
344  let method = _class.prototype[ methodName ];
345  let timeoutName = methodName + 'Timeout';
346
347  _class.prototype[ methodName ] = function() {
348    clearTimeout( this[ timeoutName ] );
349
350    let args = arguments;
351    this[ timeoutName ] = setTimeout( () => {
352      method.apply( this, args );
353      delete this[ timeoutName ];
354    }, threshold );
355  };
356};
357
358// ----- docReady ----- //
359
360utils.docReady = function( onDocReady ) {
361  let readyState = document.readyState;
362  if ( readyState == 'complete' || readyState == 'interactive' ) {
363    // do async to allow for other scripts to run. metafizzy/flickity#441
364    setTimeout( onDocReady );
365  } else {
366    document.addEventListener( 'DOMContentLoaded', onDocReady );
367  }
368};
369
370// ----- htmlInit ----- //
371
372// http://bit.ly/3oYLusc
373utils.toDashed = function( str ) {
374  return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
375    return $1 + '-' + $2;
376  } ).toLowerCase();
377};
378
379let console = global.console;
380
381// allow user to initialize classes via [data-namespace] or .js-namespace class
382// htmlInit( Widget, 'widgetName' )
383// options are parsed from data-namespace-options
384utils.htmlInit = function( WidgetClass, namespace ) {
385  utils.docReady( function() {
386    let dashedNamespace = utils.toDashed( namespace );
387    let dataAttr = 'data-' + dashedNamespace;
388    let dataAttrElems = document.querySelectorAll( `[${dataAttr}]` );
389    let jQuery = global.jQuery;
390
391    [ ...dataAttrElems ].forEach( ( elem ) => {
392      let attr = elem.getAttribute( dataAttr );
393      let options;
394      try {
395        options = attr && JSON.parse( attr );
396      } catch ( error ) {
397        // log error, do not initialize
398        if ( console ) {
399          console.error( `Error parsing ${dataAttr} on ${elem.className}: ${error}` );
400        }
401        return;
402      }
403      // initialize
404      let instance = new WidgetClass( elem, options );
405      // make available via $().data('namespace')
406      if ( jQuery ) {
407        jQuery.data( elem, namespace, instance );
408      }
409    } );
410
411  } );
412};
413
414// -----  ----- //
415
416return utils;
417
418} ) );
419// core
420( function( window, factory ) {
421  // universal module definition
422  if ( typeof module == 'object' && module.exports ) {
423    // CommonJS
424    module.exports = factory(
425        window,
426        require('ev-emitter'),
427        require('fizzy-ui-utils'),
428    );
429  } else {
430    // browser global
431    window.InfiniteScroll = factory(
432        window,
433        window.EvEmitter,
434        window.fizzyUIUtils,
435    );
436  }
437
438}( window, function factory( window, EvEmitter, utils ) {
439
440let jQuery = window.jQuery;
441// internal store of all InfiniteScroll intances
442let instances = {};
443
444function InfiniteScroll( element, options ) {
445  let queryElem = utils.getQueryElement( element );
446
447  if ( !queryElem ) {
448    console.error( 'Bad element for InfiniteScroll: ' + ( queryElem || element ) );
449    return;
450  }
451  element = queryElem;
452  // do not initialize twice on same element
453  if ( element.infiniteScrollGUID ) {
454    let instance = instances[ element.infiniteScrollGUID ];
455    instance.option( options );
456    return instance;
457  }
458
459  this.element = element;
460  // options
461  this.options = { ...InfiniteScroll.defaults };
462  this.option( options );
463  // add jQuery
464  if ( jQuery ) {
465    this.$element = jQuery( this.element );
466  }
467
468  this.create();
469}
470
471// defaults
472InfiniteScroll.defaults = {
473  // path: null,
474  // hideNav: null,
475  // debug: false,
476};
477
478// create & destroy methods
479InfiniteScroll.create = {};
480InfiniteScroll.destroy = {};
481
482let proto = InfiniteScroll.prototype;
483// inherit EvEmitter
484Object.assign( proto, EvEmitter.prototype );
485
486// --------------------------  -------------------------- //
487
488// globally unique identifiers
489let GUID = 0;
490
491proto.create = function() {
492  // create core
493  // add id for InfiniteScroll.data
494  let id = this.guid = ++GUID;
495  this.element.infiniteScrollGUID = id; // expando
496  instances[ id ] = this; // associate via id
497  // properties
498  this.pageIndex = 1; // default to first page
499  this.loadCount = 0;
500  this.updateGetPath();
501  // bail if getPath not set, or returns falsey #776
502  let hasPath = this.getPath && this.getPath();
503  if ( !hasPath ) {
504    console.error('Disabling InfiniteScroll');
505    return;
506  }
507  this.updateGetAbsolutePath();
508  this.log( 'initialized', [ this.element.className ] );
509  this.callOnInit();
510  // create features
511  for ( let method in InfiniteScroll.create ) {
512    InfiniteScroll.create[ method ].call( this );
513  }
514};
515
516proto.option = function( opts ) {
517  Object.assign( this.options, opts );
518};
519
520// call onInit option, used for binding events on init
521proto.callOnInit = function() {
522  let onInit = this.options.onInit;
523  if ( onInit ) {
524    onInit.call( this, this );
525  }
526};
527
528// ----- events ----- //
529
530proto.dispatchEvent = function( type, event, args ) {
531  this.log( type, args );
532  let emitArgs = event ? [ event ].concat( args ) : args;
533  this.emitEvent( type, emitArgs );
534  // trigger jQuery event
535  if ( !jQuery || !this.$element ) {
536    return;
537  }
538  // namespace jQuery event
539  type += '.infiniteScroll';
540  let $event = type;
541  if ( event ) {
542    // create jQuery event
543    /* eslint-disable-next-line new-cap */
544    let jQEvent = jQuery.Event( event );
545    jQEvent.type = type;
546    $event = jQEvent;
547  }
548  this.$element.trigger( $event, args );
549};
550
551let loggers = {
552  initialized: ( className ) => `on ${className}`,
553  request: ( path ) => `URL: ${path}`,
554  load: ( response, path ) => `${response.title || ''}. URL: ${path}`,
555  error: ( error, path ) => `${error}. URL: ${path}`,
556  append: ( response, path, items ) => `${items.length} items. URL: ${path}`,
557  last: ( response, path ) => `URL: ${path}`,
558  history: ( title, path ) => `URL: ${path}`,
559  pageIndex: function( index, origin ) {
560    return `current page determined to be: ${index} from ${origin}`;
561  },
562};
563
564// log events
565proto.log = function( type, args ) {
566  if ( !this.options.debug ) return;
567
568  let message = `[InfiniteScroll] ${type}`;
569  let logger = loggers[ type ];
570  if ( logger ) message += '. ' + logger.apply( this, args );
571  console.log( message );
572};
573
574// -------------------------- methods used amoung features -------------------------- //
575
576proto.updateMeasurements = function() {
577  this.windowHeight = window.innerHeight;
578  let rect = this.element.getBoundingClientRect();
579  this.top = rect.top + window.scrollY;
580};
581
582proto.updateScroller = function() {
583  let elementScroll = this.options.elementScroll;
584  if ( !elementScroll ) {
585    // default, use window
586    this.scroller = window;
587    return;
588  }
589  // if true, set to element, otherwise use option
590  this.scroller = elementScroll === true ? this.element :
591    utils.getQueryElement( elementScroll );
592  if ( !this.scroller ) {
593    throw new Error(`Unable to find elementScroll: ${elementScroll}`);
594  }
595};
596
597// -------------------------- page path -------------------------- //
598
599proto.updateGetPath = function() {
600  let optPath = this.options.path;
601  if ( !optPath ) {
602    console.error(`InfiniteScroll path option required. Set as: ${optPath}`);
603    return;
604  }
605  // function
606  let type = typeof optPath;
607  if ( type == 'function' ) {
608    this.getPath = optPath;
609    return;
610  }
611  // template string: '/pages/{{#}}.html'
612  let templateMatch = type == 'string' && optPath.match('{{#}}');
613  if ( templateMatch ) {
614    this.updateGetPathTemplate( optPath );
615    return;
616  }
617  // selector: '.next-page-selector'
618  this.updateGetPathSelector( optPath );
619};
620
621proto.updateGetPathTemplate = function( optPath ) {
622  // set getPath with template string
623  this.getPath = () => {
624    let nextIndex = this.pageIndex + 1;
625    return optPath.replace( '{{#}}', nextIndex );
626  };
627  // get pageIndex from location
628  // convert path option into regex to look for pattern in location
629  // escape query (?) in url, allows for parsing GET parameters
630  let regexString = optPath
631    .replace( /(\\\?|\?)/, '\\?' )
632    .replace( '{{#}}', '(\\d\\d?\\d?)' );
633  let templateRe = new RegExp( regexString );
634  let match = location.href.match( templateRe );
635
636  if ( match ) {
637    this.pageIndex = parseInt( match[1], 10 );
638    this.log( 'pageIndex', [ this.pageIndex, 'template string' ] );
639  }
640};
641
642let pathRegexes = [
643  // WordPress & Tumblr - example.com/page/2
644  // Jekyll - example.com/page2
645  /^(.*?\/?page\/?)(\d\d?\d?)(.*?$)/,
646  // Drupal - example.com/?page=1
647  /^(.*?\/?\?page=)(\d\d?\d?)(.*?$)/,
648  // catch all, last occurence of a number
649  /(.*?)(\d\d?\d?)(?!.*\d)(.*?$)/,
650];
651
652// try matching href to pathRegexes patterns
653let getPathParts = InfiniteScroll.getPathParts = function( href ) {
654  if ( !href ) return;
655  for ( let regex of pathRegexes ) {
656    let match = href.match( regex );
657    if ( match ) {
658      let [ , begin, index, end ] = match;
659      return { begin, index, end };
660    }
661  }
662};
663
664proto.updateGetPathSelector = function( optPath ) {
665  // parse href of link: '.next-page-link'
666  let hrefElem = document.querySelector( optPath );
667  if ( !hrefElem ) {
668    console.error(`Bad InfiniteScroll path option. Next link not found: ${optPath}`);
669    return;
670  }
671
672  let href = hrefElem.getAttribute('href');
673  let pathParts = getPathParts( href );
674  if ( !pathParts ) {
675    console.error(`InfiniteScroll unable to parse next link href: ${href}`);
676    return;
677  }
678
679  let { begin, index, end } = pathParts;
680  this.isPathSelector = true; // flag for checkLastPage()
681  this.getPath = () => begin + ( this.pageIndex + 1 ) + end;
682  // get pageIndex from href
683  this.pageIndex = parseInt( index, 10 ) - 1;
684  this.log( 'pageIndex', [ this.pageIndex, 'next link' ] );
685};
686
687proto.updateGetAbsolutePath = function() {
688  let path = this.getPath();
689  // path doesn't start with http or /
690  let isAbsolute = path.match( /^http/ ) || path.match( /^\// );
691  if ( isAbsolute ) {
692    this.getAbsolutePath = this.getPath;
693    return;
694  }
695
696  let { pathname } = location;
697  // query parameter #829. example.com/?pg=2
698  let isQuery = path.match( /^\?/ );
699  // /foo/bar/index.html => /foo/bar
700  let directory = pathname.substring( 0, pathname.lastIndexOf('/') );
701  let pathStart = isQuery ? pathname : directory + '/';
702
703  this.getAbsolutePath = () => pathStart + this.getPath();
704};
705
706// -------------------------- nav -------------------------- //
707
708// hide navigation
709InfiniteScroll.create.hideNav = function() {
710  let nav = utils.getQueryElement( this.options.hideNav );
711  if ( !nav ) return;
712
713  nav.style.display = 'none';
714  this.nav = nav;
715};
716
717InfiniteScroll.destroy.hideNav = function() {
718  if ( this.nav ) this.nav.style.display = '';
719};
720
721// -------------------------- destroy -------------------------- //
722
723proto.destroy = function() {
724  this.allOff(); // remove all event listeners
725  // call destroy methods
726  for ( let method in InfiniteScroll.destroy ) {
727    InfiniteScroll.destroy[ method ].call( this );
728  }
729
730  delete this.element.infiniteScrollGUID;
731  delete instances[ this.guid ];
732  // remove jQuery data. #807
733  if ( jQuery && this.$element ) {
734    jQuery.removeData( this.element, 'infiniteScroll' );
735  }
736};
737
738// -------------------------- utilities -------------------------- //
739
740// https://remysharp.com/2010/07/21/throttling-function-calls
741InfiniteScroll.throttle = function( fn, threshold ) {
742  threshold = threshold || 200;
743  let last, timeout;
744
745  return function() {
746    let now = +new Date();
747    let args = arguments;
748    let trigger = () => {
749      last = now;
750      fn.apply( this, args );
751    };
752    if ( last && now < last + threshold ) {
753      // hold on to it
754      clearTimeout( timeout );
755      timeout = setTimeout( trigger, threshold );
756    } else {
757      trigger();
758    }
759  };
760};
761
762InfiniteScroll.data = function( elem ) {
763  elem = utils.getQueryElement( elem );
764  let id = elem && elem.infiniteScrollGUID;
765  return id && instances[ id ];
766};
767
768// set internal jQuery, for Webpack + jQuery v3
769InfiniteScroll.setJQuery = function( jqry ) {
770  jQuery = jqry;
771};
772
773// -------------------------- setup -------------------------- //
774
775utils.htmlInit( InfiniteScroll, 'infinite-scroll' );
776
777// add noop _init method for jQuery Bridget. #768
778proto._init = function() {};
779
780let { jQueryBridget } = window;
781if ( jQuery && jQueryBridget ) {
782  jQueryBridget( 'infiniteScroll', InfiniteScroll, jQuery );
783}
784
785// --------------------------  -------------------------- //
786
787return InfiniteScroll;
788
789} ) );
790// page-load
791( function( window, factory ) {
792  // universal module definition
793  if ( typeof module == 'object' && module.exports ) {
794    // CommonJS
795    module.exports = factory(
796        window,
797        require('./core'),
798    );
799  } else {
800    // browser global
801    factory(
802        window,
803        window.InfiniteScroll,
804    );
805  }
806
807}( window, function factory( window, InfiniteScroll ) {
808
809let proto = InfiniteScroll.prototype;
810
811Object.assign( InfiniteScroll.defaults, {
812  // append: false,
813  loadOnScroll: true,
814  checkLastPage: true,
815  responseBody: 'text',
816  domParseResponse: true,
817  // prefill: false,
818  // outlayer: null,
819} );
820
821InfiniteScroll.create.pageLoad = function() {
822  this.canLoad = true;
823  this.on( 'scrollThreshold', this.onScrollThresholdLoad );
824  this.on( 'load', this.checkLastPage );
825  if ( this.options.outlayer ) {
826    this.on( 'append', this.onAppendOutlayer );
827  }
828};
829
830proto.onScrollThresholdLoad = function() {
831  if ( this.options.loadOnScroll ) this.loadNextPage();
832};
833
834let domParser = new DOMParser();
835
836proto.loadNextPage = function() {
837  if ( this.isLoading || !this.canLoad ) return;
838
839  let { responseBody, domParseResponse, fetchOptions } = this.options;
840  let path = this.getAbsolutePath();
841  this.isLoading = true;
842  if ( typeof fetchOptions == 'function' ) fetchOptions = fetchOptions();
843
844  let fetchPromise = fetch( path, fetchOptions )
845    .then( ( response ) => {
846      if ( !response.ok ) {
847        let error = new Error( response.statusText );
848        this.onPageError( error, path, response );
849        return { response };
850      }
851
852      return response[ responseBody ]().then( ( body ) => {
853        let canDomParse = responseBody == 'text' && domParseResponse;
854        if ( canDomParse ) {
855          body = domParser.parseFromString( body, 'text/html' );
856        }
857        if ( response.status == 204 ) {
858          this.lastPageReached( body, path );
859          return { body, response };
860        } else {
861          return this.onPageLoad( body, path, response );
862        }
863      } );
864    } )
865    .catch( ( error ) => {
866      this.onPageError( error, path );
867    } );
868
869  this.dispatchEvent( 'request', null, [ path, fetchPromise ] );
870
871  return fetchPromise;
872};
873
874proto.onPageLoad = function( body, path, response ) {
875  // done loading if not appending
876  if ( !this.options.append ) {
877    this.isLoading = false;
878  }
879  this.pageIndex++;
880  this.loadCount++;
881  this.dispatchEvent( 'load', null, [ body, path, response ] );
882  return this.appendNextPage( body, path, response );
883};
884
885proto.appendNextPage = function( body, path, response ) {
886  let { append, responseBody, domParseResponse } = this.options;
887  // do not append json
888  let isDocument = responseBody == 'text' && domParseResponse;
889  if ( !isDocument || !append ) return { body, response };
890
891  let items = body.querySelectorAll( append );
892  let promiseValue = { body, response, items };
893  // last page hit if no items. #840
894  if ( !items || !items.length ) {
895    this.lastPageReached( body, path );
896    return promiseValue;
897  }
898
899  let fragment = getItemsFragment( items );
900  let appendReady = () => {
901    this.appendItems( items, fragment );
902    this.isLoading = false;
903    this.dispatchEvent( 'append', null, [ body, path, items, response ] );
904    return promiseValue;
905  };
906
907  // TODO add hook for option to trigger appendReady
908  if ( this.options.outlayer ) {
909    return this.appendOutlayerItems( fragment, appendReady );
910  } else {
911    return appendReady();
912  }
913};
914
915proto.appendItems = function( items, fragment ) {
916  if ( !items || !items.length ) return;
917
918  // get fragment if not provided
919  fragment = fragment || getItemsFragment( items );
920  refreshScripts( fragment );
921  this.element.appendChild( fragment );
922};
923
924function getItemsFragment( items ) {
925  // add items to fragment
926  let fragment = document.createDocumentFragment();
927  if ( items ) fragment.append( ...items );
928  return fragment;
929}
930
931// replace <script>s with copies so they load
932// <script>s added by InfiniteScroll will not load
933// similar to https://stackoverflow.com/questions/610995
934function refreshScripts( fragment ) {
935  let scripts = fragment.querySelectorAll('script');
936  for ( let script of scripts ) {
937    let freshScript = document.createElement('script');
938    // copy attributes
939    let attrs = script.attributes;
940    for ( let attr of attrs ) {
941      freshScript.setAttribute( attr.name, attr.value );
942    }
943    // copy inner script code. #718, #782
944    freshScript.innerHTML = script.innerHTML;
945    script.parentNode.replaceChild( freshScript, script );
946  }
947}
948
949// ----- outlayer ----- //
950
951proto.appendOutlayerItems = function( fragment, appendReady ) {
952  let imagesLoaded = InfiniteScroll.imagesLoaded || window.imagesLoaded;
953  if ( !imagesLoaded ) {
954    console.error('[InfiniteScroll] imagesLoaded required for outlayer option');
955    this.isLoading = false;
956    return;
957  }
958  // append once images loaded
959  return new Promise( function( resolve ) {
960    imagesLoaded( fragment, function() {
961      let bodyResponse = appendReady();
962      resolve( bodyResponse );
963    } );
964  } );
965};
966
967proto.onAppendOutlayer = function( response, path, items ) {
968  this.options.outlayer.appended( items );
969};
970
971// ----- checkLastPage ----- //
972
973// check response for next element
974proto.checkLastPage = function( body, path ) {
975  let { checkLastPage, path: pathOpt } = this.options;
976  if ( !checkLastPage ) return;
977
978  // if path is function, check if next path is truthy
979  if ( typeof pathOpt == 'function' ) {
980    let nextPath = this.getPath();
981    if ( !nextPath ) {
982      this.lastPageReached( body, path );
983      return;
984    }
985  }
986  // get selector from checkLastPage or path option
987  let selector;
988  if ( typeof checkLastPage == 'string' ) {
989    selector = checkLastPage;
990  } else if ( this.isPathSelector ) {
991    // path option is selector string
992    selector = pathOpt;
993  }
994  // check last page for selector
995  // bail if no selector or not document response
996  if ( !selector || !body.querySelector ) return;
997
998  // check if response has selector
999  let nextElem = body.querySelector( selector );
1000  if ( !nextElem ) this.lastPageReached( body, path );
1001};
1002
1003proto.lastPageReached = function( body, path ) {
1004  this.canLoad = false;
1005  this.dispatchEvent( 'last', null, [ body, path ] );
1006};
1007
1008// ----- error ----- //
1009
1010proto.onPageError = function( error, path, response ) {
1011  this.isLoading = false;
1012  this.canLoad = false;
1013  this.dispatchEvent( 'error', null, [ error, path, response ] );
1014  return error;
1015};
1016
1017// -------------------------- prefill -------------------------- //
1018
1019InfiniteScroll.create.prefill = function() {
1020  if ( !this.options.prefill ) return;
1021
1022  let append = this.options.append;
1023  if ( !append ) {
1024    console.error(`append option required for prefill. Set as :${append}`);
1025    return;
1026  }
1027  this.updateMeasurements();
1028  this.updateScroller();
1029  this.isPrefilling = true;
1030  this.on( 'append', this.prefill );
1031  this.once( 'error', this.stopPrefill );
1032  this.once( 'last', this.stopPrefill );
1033  this.prefill();
1034};
1035
1036proto.prefill = function() {
1037  let distance = this.getPrefillDistance();
1038  this.isPrefilling = distance >= 0;
1039  if ( this.isPrefilling ) {
1040    this.log('prefill');
1041    this.loadNextPage();
1042  } else {
1043    this.stopPrefill();
1044  }
1045};
1046
1047proto.getPrefillDistance = function() {
1048  // element scroll
1049  if ( this.options.elementScroll ) {
1050    return this.scroller.clientHeight - this.scroller.scrollHeight;
1051  }
1052  // window
1053  return this.windowHeight - this.element.clientHeight;
1054};
1055
1056proto.stopPrefill = function() {
1057  this.log('stopPrefill');
1058  this.off( 'append', this.prefill );
1059};
1060
1061// --------------------------  -------------------------- //
1062
1063return InfiniteScroll;
1064
1065} ) );
1066// scroll-watch
1067( function( window, factory ) {
1068  // universal module definition
1069  if ( typeof module == 'object' && module.exports ) {
1070    // CommonJS
1071    module.exports = factory(
1072        window,
1073        require('./core'),
1074        require('fizzy-ui-utils'),
1075    );
1076  } else {
1077    // browser global
1078    factory(
1079        window,
1080        window.InfiniteScroll,
1081        window.fizzyUIUtils,
1082    );
1083  }
1084
1085}( window, function factory( window, InfiniteScroll, utils ) {
1086
1087let proto = InfiniteScroll.prototype;
1088
1089// default options
1090Object.assign( InfiniteScroll.defaults, {
1091  scrollThreshold: 400,
1092  // elementScroll: null,
1093} );
1094
1095InfiniteScroll.create.scrollWatch = function() {
1096  // events
1097  this.pageScrollHandler = this.onPageScroll.bind( this );
1098  this.resizeHandler = this.onResize.bind( this );
1099
1100  let scrollThreshold = this.options.scrollThreshold;
1101  let isEnable = scrollThreshold || scrollThreshold === 0;
1102  if ( isEnable ) this.enableScrollWatch();
1103};
1104
1105InfiniteScroll.destroy.scrollWatch = function() {
1106  this.disableScrollWatch();
1107};
1108
1109proto.enableScrollWatch = function() {
1110  if ( this.isScrollWatching ) return;
1111
1112  this.isScrollWatching = true;
1113  this.updateMeasurements();
1114  this.updateScroller();
1115  // TODO disable after error?
1116  this.on( 'last', this.disableScrollWatch );
1117  this.bindScrollWatchEvents( true );
1118};
1119
1120proto.disableScrollWatch = function() {
1121  if ( !this.isScrollWatching ) return;
1122
1123  this.bindScrollWatchEvents( false );
1124  delete this.isScrollWatching;
1125};
1126
1127proto.bindScrollWatchEvents = function( isBind ) {
1128  let addRemove = isBind ? 'addEventListener' : 'removeEventListener';
1129  this.scroller[ addRemove ]( 'scroll', this.pageScrollHandler );
1130  window[ addRemove ]( 'resize', this.resizeHandler );
1131};
1132
1133proto.onPageScroll = InfiniteScroll.throttle( function() {
1134  let distance = this.getBottomDistance();
1135  if ( distance <= this.options.scrollThreshold ) {
1136    this.dispatchEvent('scrollThreshold');
1137  }
1138} );
1139
1140proto.getBottomDistance = function() {
1141  let bottom, scrollY;
1142  if ( this.options.elementScroll ) {
1143    bottom = this.scroller.scrollHeight;
1144    scrollY = this.scroller.scrollTop + this.scroller.clientHeight;
1145  } else {
1146    bottom = this.top + this.element.clientHeight;
1147    scrollY = window.scrollY + this.windowHeight;
1148  }
1149  return bottom - scrollY;
1150};
1151
1152proto.onResize = function() {
1153  this.updateMeasurements();
1154};
1155
1156utils.debounceMethod( InfiniteScroll, 'onResize', 150 );
1157
1158// --------------------------  -------------------------- //
1159
1160return InfiniteScroll;
1161
1162} ) );
1163// history
1164( function( window, factory ) {
1165  // universal module definition
1166  if ( typeof module == 'object' && module.exports ) {
1167    // CommonJS
1168    module.exports = factory(
1169        window,
1170        require('./core'),
1171        require('fizzy-ui-utils'),
1172    );
1173  } else {
1174    // browser global
1175    factory(
1176        window,
1177        window.InfiniteScroll,
1178        window.fizzyUIUtils,
1179    );
1180  }
1181
1182}( window, function factory( window, InfiniteScroll, utils ) {
1183
1184let proto = InfiniteScroll.prototype;
1185
1186Object.assign( InfiniteScroll.defaults, {
1187  history: 'replace',
1188  // historyTitle: false,
1189} );
1190
1191let link = document.createElement('a');
1192
1193// ----- create/destroy ----- //
1194
1195InfiniteScroll.create.history = function() {
1196  if ( !this.options.history ) return;
1197
1198  // check for same origin
1199  link.href = this.getAbsolutePath();
1200  // MS Edge does not have origin on link
1201  // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12236493/
1202  let linkOrigin = link.origin || link.protocol + '//' + link.host;
1203  let isSameOrigin = linkOrigin == location.origin;
1204  if ( !isSameOrigin ) {
1205    console.error( '[InfiniteScroll] cannot set history with different origin: ' +
1206      `${link.origin} on ${location.origin} . History behavior disabled.` );
1207    return;
1208  }
1209
1210  // two ways to handle changing history
1211  if ( this.options.append ) {
1212    this.createHistoryAppend();
1213  } else {
1214    this.createHistoryPageLoad();
1215  }
1216};
1217
1218proto.createHistoryAppend = function() {
1219  this.updateMeasurements();
1220  this.updateScroller();
1221  // array of scroll positions of appended pages
1222  this.scrollPages = [
1223    // first page
1224    {
1225      top: 0,
1226      path: location.href,
1227      title: document.title,
1228    },
1229  ];
1230  this.scrollPage = this.scrollPages[0];
1231  // events
1232  this.scrollHistoryHandler = this.onScrollHistory.bind( this );
1233  this.unloadHandler = this.onUnload.bind( this );
1234  this.scroller.addEventListener( 'scroll', this.scrollHistoryHandler );
1235  this.on( 'append', this.onAppendHistory );
1236  this.bindHistoryAppendEvents( true );
1237};
1238
1239proto.bindHistoryAppendEvents = function( isBind ) {
1240  let addRemove = isBind ? 'addEventListener' : 'removeEventListener';
1241  this.scroller[ addRemove ]( 'scroll', this.scrollHistoryHandler );
1242  window[ addRemove ]( 'unload', this.unloadHandler );
1243};
1244
1245proto.createHistoryPageLoad = function() {
1246  this.on( 'load', this.onPageLoadHistory );
1247};
1248
1249InfiniteScroll.destroy.history =
1250proto.destroyHistory = function() {
1251  let isHistoryAppend = this.options.history && this.options.append;
1252  if ( isHistoryAppend ) {
1253    this.bindHistoryAppendEvents( false );
1254  }
1255};
1256
1257// ----- append history ----- //
1258
1259proto.onAppendHistory = function( response, path, items ) {
1260  // do not proceed if no items. #779
1261  if ( !items || !items.length ) return;
1262
1263  let firstItem = items[0];
1264  let elemScrollY = this.getElementScrollY( firstItem );
1265  // resolve path
1266  link.href = path;
1267  // add page data to hash
1268  this.scrollPages.push({
1269    top: elemScrollY,
1270    path: link.href,
1271    title: response.title,
1272  });
1273};
1274
1275proto.getElementScrollY = function( elem ) {
1276  if ( this.options.elementScroll ) {
1277    return elem.offsetTop - this.top;
1278  } else {
1279    let rect = elem.getBoundingClientRect();
1280    return rect.top + window.scrollY;
1281  }
1282};
1283
1284proto.onScrollHistory = function() {
1285  // cycle through positions, find biggest without going over
1286  let scrollPage = this.getClosestScrollPage();
1287  // set history if changed
1288  if ( scrollPage != this.scrollPage ) {
1289    this.scrollPage = scrollPage;
1290    this.setHistory( scrollPage.title, scrollPage.path );
1291  }
1292};
1293
1294utils.debounceMethod( InfiniteScroll, 'onScrollHistory', 150 );
1295
1296proto.getClosestScrollPage = function() {
1297  let scrollViewY;
1298  if ( this.options.elementScroll ) {
1299    scrollViewY = this.scroller.scrollTop + this.scroller.clientHeight / 2;
1300  } else {
1301    scrollViewY = window.scrollY + this.windowHeight / 2;
1302  }
1303
1304  let scrollPage;
1305  for ( let page of this.scrollPages ) {
1306    if ( page.top >= scrollViewY ) break;
1307
1308    scrollPage = page;
1309  }
1310  return scrollPage;
1311};
1312
1313proto.setHistory = function( title, path ) {
1314  let optHistory = this.options.history;
1315  let historyMethod = optHistory && history[ optHistory + 'State' ];
1316  if ( !historyMethod ) return;
1317
1318  history[ optHistory + 'State' ]( null, title, path );
1319  if ( this.options.historyTitle ) document.title = title;
1320  this.dispatchEvent( 'history', null, [ title, path ] );
1321};
1322
1323// scroll to top to prevent initial scroll-reset after page refresh
1324// https://stackoverflow.com/a/18633915/182183
1325proto.onUnload = function() {
1326  if ( this.scrollPage.top === 0 ) return;
1327
1328  // calculate where scroll position would be on refresh
1329  let scrollY = window.scrollY - this.scrollPage.top + this.top;
1330  // disable scroll event before setting scroll #679
1331  this.destroyHistory();
1332  scrollTo( 0, scrollY );
1333};
1334
1335// ----- load history ----- //
1336
1337// update URL
1338proto.onPageLoadHistory = function( response, path ) {
1339  this.setHistory( response.title, path );
1340};
1341
1342// --------------------------  -------------------------- //
1343
1344return InfiniteScroll;
1345
1346} ) );
1347// button
1348( function( window, factory ) {
1349  // universal module definition
1350  if ( typeof module == 'object' && module.exports ) {
1351    // CommonJS
1352    module.exports = factory(
1353        window,
1354        require('./core'),
1355        require('fizzy-ui-utils'),
1356    );
1357  } else {
1358    // browser global
1359    factory(
1360        window,
1361        window.InfiniteScroll,
1362        window.fizzyUIUtils,
1363    );
1364  }
1365
1366}( window, function factory( window, InfiniteScroll, utils ) {
1367
1368// -------------------------- InfiniteScrollButton -------------------------- //
1369
1370class InfiniteScrollButton {
1371  constructor( element, infScroll ) {
1372    this.element = element;
1373    this.infScroll = infScroll;
1374    // events
1375    this.clickHandler = this.onClick.bind( this );
1376    this.element.addEventListener( 'click', this.clickHandler );
1377    infScroll.on( 'request', this.disable.bind( this ) );
1378    infScroll.on( 'load', this.enable.bind( this ) );
1379    infScroll.on( 'error', this.hide.bind( this ) );
1380    infScroll.on( 'last', this.hide.bind( this ) );
1381  }
1382
1383  onClick( event ) {
1384    event.preventDefault();
1385    this.infScroll.loadNextPage();
1386  }
1387
1388  enable() {
1389    this.element.removeAttribute('disabled');
1390  }
1391
1392  disable() {
1393    this.element.disabled = 'disabled';
1394  }
1395
1396  hide() {
1397    this.element.style.display = 'none';
1398  }
1399
1400  destroy() {
1401    this.element.removeEventListener( 'click', this.clickHandler );
1402  }
1403
1404}
1405
1406// -------------------------- InfiniteScroll methods -------------------------- //
1407
1408// InfiniteScroll.defaults.button = null;
1409
1410InfiniteScroll.create.button = function() {
1411  let buttonElem = utils.getQueryElement( this.options.button );
1412  if ( buttonElem ) {
1413    this.button = new InfiniteScrollButton( buttonElem, this );
1414  }
1415};
1416
1417InfiniteScroll.destroy.button = function() {
1418  if ( this.button ) this.button.destroy();
1419};
1420
1421// --------------------------  -------------------------- //
1422
1423InfiniteScroll.Button = InfiniteScrollButton;
1424
1425return InfiniteScroll;
1426
1427} ) );
1428// status
1429( function( window, factory ) {
1430  // universal module definition
1431  if ( typeof module == 'object' && module.exports ) {
1432    // CommonJS
1433    module.exports = factory(
1434        window,
1435        require('./core'),
1436        require('fizzy-ui-utils'),
1437    );
1438  } else {
1439    // browser global
1440    factory(
1441        window,
1442        window.InfiniteScroll,
1443        window.fizzyUIUtils,
1444    );
1445  }
1446
1447}( window, function factory( window, InfiniteScroll, utils ) {
1448
1449let proto = InfiniteScroll.prototype;
1450
1451// InfiniteScroll.defaults.status = null;
1452
1453InfiniteScroll.create.status = function() {
1454  let statusElem = utils.getQueryElement( this.options.status );
1455  if ( !statusElem ) return;
1456
1457  // elements
1458  this.statusElement = statusElem;
1459  this.statusEventElements = {
1460    request: statusElem.querySelector('.infinite-scroll-request'),
1461    error: statusElem.querySelector('.infinite-scroll-error'),
1462    last: statusElem.querySelector('.infinite-scroll-last'),
1463  };
1464  // events
1465  this.on( 'request', this.showRequestStatus );
1466  this.on( 'error', this.showErrorStatus );
1467  this.on( 'last', this.showLastStatus );
1468  this.bindHideStatus('on');
1469};
1470
1471proto.bindHideStatus = function( bindMethod ) {
1472  let hideEvent = this.options.append ? 'append' : 'load';
1473  this[ bindMethod ]( hideEvent, this.hideAllStatus );
1474};
1475
1476proto.showRequestStatus = function() {
1477  this.showStatus('request');
1478};
1479
1480proto.showErrorStatus = function() {
1481  this.showStatus('error');
1482};
1483
1484proto.showLastStatus = function() {
1485  this.showStatus('last');
1486  // prevent last then append event race condition from showing last status #706
1487  this.bindHideStatus('off');
1488};
1489
1490proto.showStatus = function( eventName ) {
1491  show( this.statusElement );
1492  this.hideStatusEventElements();
1493  let eventElem = this.statusEventElements[ eventName ];
1494  show( eventElem );
1495};
1496
1497proto.hideAllStatus = function() {
1498  hide( this.statusElement );
1499  this.hideStatusEventElements();
1500};
1501
1502proto.hideStatusEventElements = function() {
1503  for ( let type in this.statusEventElements ) {
1504    let eventElem = this.statusEventElements[ type ];
1505    hide( eventElem );
1506  }
1507};
1508
1509// --------------------------  -------------------------- //
1510
1511function hide( elem ) {
1512  setDisplay( elem, 'none' );
1513}
1514
1515function show( elem ) {
1516  setDisplay( elem, 'block' );
1517}
1518
1519function setDisplay( elem, value ) {
1520  if ( elem ) {
1521    elem.style.display = value;
1522  }
1523}
1524
1525// --------------------------  -------------------------- //
1526
1527return InfiniteScroll;
1528
1529} ) );
1530/*!
1531 * imagesLoaded v4.1.4
1532 * JavaScript is all like "You images are done yet or what?"
1533 * MIT License
1534 */
1535
1536( function( window, factory ) { 'use strict';
1537  // universal module definition
1538
1539  /*global define: false, module: false, require: false */
1540
1541  if ( typeof define == 'function' && define.amd ) {
1542    // AMD
1543    define( [
1544      'ev-emitter/ev-emitter'
1545    ], function( EvEmitter ) {
1546      return factory( window, EvEmitter );
1547    });
1548  } else if ( typeof module == 'object' && module.exports ) {
1549    // CommonJS
1550    module.exports = factory(
1551      window,
1552      require('ev-emitter')
1553    );
1554  } else {
1555    // browser global
1556    window.imagesLoaded = factory(
1557      window,
1558      window.EvEmitter
1559    );
1560  }
1561
1562})( typeof window !== 'undefined' ? window : this,
1563
1564// --------------------------  factory -------------------------- //
1565
1566function factory( window, EvEmitter ) {
1567
1568'use strict';
1569
1570var $ = window.jQuery;
1571var console = window.console;
1572
1573// -------------------------- helpers -------------------------- //
1574
1575// extend objects
1576function extend( a, b ) {
1577  for ( var prop in b ) {
1578    a[ prop ] = b[ prop ];
1579  }
1580  return a;
1581}
1582
1583var arraySlice = Array.prototype.slice;
1584
1585// turn element or nodeList into an array
1586function makeArray( obj ) {
1587  if ( Array.isArray( obj ) ) {
1588    // use object if already an array
1589    return obj;
1590  }
1591
1592  var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
1593  if ( isArrayLike ) {
1594    // convert nodeList to array
1595    return arraySlice.call( obj );
1596  }
1597
1598  // array of single index
1599  return [ obj ];
1600}
1601
1602// -------------------------- imagesLoaded -------------------------- //
1603
1604/**
1605 * @param {Array, Element, NodeList, String} elem
1606 * @param {Object or Function} options - if function, use as callback
1607 * @param {Function} onAlways - callback function
1608 */
1609function ImagesLoaded( elem, options, onAlways ) {
1610  // coerce ImagesLoaded() without new, to be new ImagesLoaded()
1611  if ( !( this instanceof ImagesLoaded ) ) {
1612    return new ImagesLoaded( elem, options, onAlways );
1613  }
1614  // use elem as selector string
1615  var queryElem = elem;
1616  if ( typeof elem == 'string' ) {
1617    queryElem = document.querySelectorAll( elem );
1618  }
1619  // bail if bad element
1620  if ( !queryElem ) {
1621    console.error( 'Bad element for imagesLoaded ' + ( queryElem || elem ) );
1622    return;
1623  }
1624
1625  this.elements = makeArray( queryElem );
1626  this.options = extend( {}, this.options );
1627  // shift arguments if no options set
1628  if ( typeof options == 'function' ) {
1629    onAlways = options;
1630  } else {
1631    extend( this.options, options );
1632  }
1633
1634  if ( onAlways ) {
1635    this.on( 'always', onAlways );
1636  }
1637
1638  this.getImages();
1639
1640  if ( $ ) {
1641    // add jQuery Deferred object
1642    this.jqDeferred = new $.Deferred();
1643  }
1644
1645  // HACK check async to allow time to bind listeners
1646  setTimeout( this.check.bind( this ) );
1647}
1648
1649ImagesLoaded.prototype = Object.create( EvEmitter.prototype );
1650
1651ImagesLoaded.prototype.options = {};
1652
1653ImagesLoaded.prototype.getImages = function() {
1654  this.images = [];
1655
1656  // filter & find items if we have an item selector
1657  this.elements.forEach( this.addElementImages, this );
1658};
1659
1660/**
1661 * @param {Node} element
1662 */
1663ImagesLoaded.prototype.addElementImages = function( elem ) {
1664  // filter siblings
1665  if ( elem.nodeName == 'IMG' ) {
1666    this.addImage( elem );
1667  }
1668  // get background image on element
1669  if ( this.options.background === true ) {
1670    this.addElementBackgroundImages( elem );
1671  }
1672
1673  // find children
1674  // no non-element nodes, #143
1675  var nodeType = elem.nodeType;
1676  if ( !nodeType || !elementNodeTypes[ nodeType ] ) {
1677    return;
1678  }
1679  var childImgs = elem.querySelectorAll('img');
1680  // concat childElems to filterFound array
1681  for ( var i=0; i < childImgs.length; i++ ) {
1682    var img = childImgs[i];
1683    this.addImage( img );
1684  }
1685
1686  // get child background images
1687  if ( typeof this.options.background == 'string' ) {
1688    var children = elem.querySelectorAll( this.options.background );
1689    for ( i=0; i < children.length; i++ ) {
1690      var child = children[i];
1691      this.addElementBackgroundImages( child );
1692    }
1693  }
1694};
1695
1696var elementNodeTypes = {
1697  1: true,
1698  9: true,
1699  11: true
1700};
1701
1702ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
1703  var style = getComputedStyle( elem );
1704  if ( !style ) {
1705    // Firefox returns null if in a hidden iframe https://bugzil.la/548397
1706    return;
1707  }
1708  // get url inside url("...")
1709  var reURL = /url\((['"])?(.*?)\1\)/gi;
1710  var matches = reURL.exec( style.backgroundImage );
1711  while ( matches !== null ) {
1712    var url = matches && matches[2];
1713    if ( url ) {
1714      this.addBackground( url, elem );
1715    }
1716    matches = reURL.exec( style.backgroundImage );
1717  }
1718};
1719
1720/**
1721 * @param {Image} img
1722 */
1723ImagesLoaded.prototype.addImage = function( img ) {
1724  var loadingImage = new LoadingImage( img );
1725  this.images.push( loadingImage );
1726};
1727
1728ImagesLoaded.prototype.addBackground = function( url, elem ) {
1729  var background = new Background( url, elem );
1730  this.images.push( background );
1731};
1732
1733ImagesLoaded.prototype.check = function() {
1734  var _this = this;
1735  this.progressedCount = 0;
1736  this.hasAnyBroken = false;
1737  // complete if no images
1738  if ( !this.images.length ) {
1739    this.complete();
1740    return;
1741  }
1742
1743  function onProgress( image, elem, message ) {
1744    // HACK - Chrome triggers event before object properties have changed. #83
1745    setTimeout( function() {
1746      _this.progress( image, elem, message );
1747    });
1748  }
1749
1750  this.images.forEach( function( loadingImage ) {
1751    loadingImage.once( 'progress', onProgress );
1752    loadingImage.check();
1753  });
1754};
1755
1756ImagesLoaded.prototype.progress = function( image, elem, message ) {
1757  this.progressedCount++;
1758  this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
1759  // progress event
1760  this.emitEvent( 'progress', [ this, image, elem ] );
1761  if ( this.jqDeferred && this.jqDeferred.notify ) {
1762    this.jqDeferred.notify( this, image );
1763  }
1764  // check if completed
1765  if ( this.progressedCount == this.images.length ) {
1766    this.complete();
1767  }
1768
1769  if ( this.options.debug && console ) {
1770    console.log( 'progress: ' + message, image, elem );
1771  }
1772};
1773
1774ImagesLoaded.prototype.complete = function() {
1775  var eventName = this.hasAnyBroken ? 'fail' : 'done';
1776  this.isComplete = true;
1777  this.emitEvent( eventName, [ this ] );
1778  this.emitEvent( 'always', [ this ] );
1779  if ( this.jqDeferred ) {
1780    var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';
1781    this.jqDeferred[ jqMethod ]( this );
1782  }
1783};
1784
1785// --------------------------  -------------------------- //
1786
1787function LoadingImage( img ) {
1788  this.img = img;
1789}
1790
1791LoadingImage.prototype = Object.create( EvEmitter.prototype );
1792
1793LoadingImage.prototype.check = function() {
1794  // If complete is true and browser supports natural sizes,
1795  // try to check for image status manually.
1796  var isComplete = this.getIsImageComplete();
1797  if ( isComplete ) {
1798    // report based on naturalWidth
1799    this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
1800    return;
1801  }
1802
1803  // If none of the checks above matched, simulate loading on detached element.
1804  this.proxyImage = new Image();
1805  this.proxyImage.addEventListener( 'load', this );
1806  this.proxyImage.addEventListener( 'error', this );
1807  // bind to image as well for Firefox. #191
1808  this.img.addEventListener( 'load', this );
1809  this.img.addEventListener( 'error', this );
1810  this.proxyImage.src = this.img.src;
1811};
1812
1813LoadingImage.prototype.getIsImageComplete = function() {
1814  // check for non-zero, non-undefined naturalWidth
1815  // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671
1816  return this.img.complete && this.img.naturalWidth;
1817};
1818
1819LoadingImage.prototype.confirm = function( isLoaded, message ) {
1820  this.isLoaded = isLoaded;
1821  this.emitEvent( 'progress', [ this, this.img, message ] );
1822};
1823
1824// ----- events ----- //
1825
1826// trigger specified handler for event type
1827LoadingImage.prototype.handleEvent = function( event ) {
1828  var method = 'on' + event.type;
1829  if ( this[ method ] ) {
1830    this[ method ]( event );
1831  }
1832};
1833
1834LoadingImage.prototype.onload = function() {
1835  this.confirm( true, 'onload' );
1836  this.unbindEvents();
1837};
1838
1839LoadingImage.prototype.onerror = function() {
1840  this.confirm( false, 'onerror' );
1841  this.unbindEvents();
1842};
1843
1844LoadingImage.prototype.unbindEvents = function() {
1845  this.proxyImage.removeEventListener( 'load', this );
1846  this.proxyImage.removeEventListener( 'error', this );
1847  this.img.removeEventListener( 'load', this );
1848  this.img.removeEventListener( 'error', this );
1849};
1850
1851// -------------------------- Background -------------------------- //
1852
1853function Background( url, element ) {
1854  this.url = url;
1855  this.element = element;
1856  this.img = new Image();
1857}
1858
1859// inherit LoadingImage prototype
1860Background.prototype = Object.create( LoadingImage.prototype );
1861
1862Background.prototype.check = function() {
1863  this.img.addEventListener( 'load', this );
1864  this.img.addEventListener( 'error', this );
1865  this.img.src = this.url;
1866  // check if image is already complete
1867  var isComplete = this.getIsImageComplete();
1868  if ( isComplete ) {
1869    this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
1870    this.unbindEvents();
1871  }
1872};
1873
1874Background.prototype.unbindEvents = function() {
1875  this.img.removeEventListener( 'load', this );
1876  this.img.removeEventListener( 'error', this );
1877};
1878
1879Background.prototype.confirm = function( isLoaded, message ) {
1880  this.isLoaded = isLoaded;
1881  this.emitEvent( 'progress', [ this, this.element, message ] );
1882};
1883
1884// -------------------------- jQuery -------------------------- //
1885
1886ImagesLoaded.makeJQueryPlugin = function( jQuery ) {
1887  jQuery = jQuery || window.jQuery;
1888  if ( !jQuery ) {
1889    return;
1890  }
1891  // set local variable
1892  $ = jQuery;
1893  // $().imagesLoaded()
1894  $.fn.imagesLoaded = function( options, callback ) {
1895    var instance = new ImagesLoaded( this, options, callback );
1896    return instance.jqDeferred.promise( $(this) );
1897  };
1898};
1899// try making plugin
1900ImagesLoaded.makeJQueryPlugin();
1901
1902// --------------------------  -------------------------- //
1903
1904return ImagesLoaded;
1905
1906});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.