| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442 |
- /*!
- Images Loaded Packaged
- Version: v5.0.0
- Plugin URL: https://imagesloaded.desandro.com/
- License: Copyright 2021-2024 | Released under the MIT License
- !*/
- /**
- * EvEmitter v2.1.1
- * Lil' event emitter
- * MIT License
- */
- ( function( global, factory ) {
- // universal module definition
- 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() {}
- let proto = EvEmitter.prototype;
- proto.on = function( eventName, listener ) {
- if ( !eventName || !listener ) return this;
- // set events hash
- let events = this._events = this._events || {};
- // set listeners array
- let listeners = events[ eventName ] = events[ eventName ] || [];
- // only add once
- if ( !listeners.includes( listener ) ) {
- listeners.push( listener );
- }
- return this;
- };
- proto.once = function( eventName, listener ) {
- if ( !eventName || !listener ) return this;
- // add event
- this.on( eventName, listener );
- // set once flag
- // set onceEvents hash
- let onceEvents = this._onceEvents = this._onceEvents || {};
- // set onceListeners object
- let onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
- // set flag
- onceListeners[ listener ] = true;
- return this;
- };
- proto.off = function( eventName, listener ) {
- let listeners = this._events && this._events[ eventName ];
- if ( !listeners || !listeners.length ) return this;
- let index = listeners.indexOf( listener );
- if ( index != -1 ) {
- listeners.splice( index, 1 );
- }
- return this;
- };
- proto.emitEvent = function( eventName, args ) {
- let listeners = this._events && this._events[ eventName ];
- if ( !listeners || !listeners.length ) return this;
- // copy over to avoid interference if .off() in listener
- listeners = listeners.slice( 0 );
- args = args || [];
- // once stuff
- let onceListeners = this._onceEvents && this._onceEvents[ eventName ];
- for ( let listener of listeners ) {
- let 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 );
- }
- return this;
- };
- proto.allOff = function() {
- delete this._events;
- delete this._onceEvents;
- return this;
- };
- return EvEmitter;
- } ) );
- /*!
- * imagesLoaded v5.0.0
- * JavaScript is all like "You images are done yet or what?"
- * MIT License
- */
- ( function( window, factory ) {
- // universal module definition
- 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,
- function factory( window, EvEmitter ) {
- let $ = window.jQuery;
- let console = window.console;
- // -------------------------- helpers -------------------------- //
- // turn element or nodeList into an array
- function makeArray( obj ) {
- // use object if already an array
- if ( Array.isArray( obj ) ) return obj;
- let isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
- // convert nodeList to array
- if ( isArrayLike ) return [ ...obj ];
- // array of single index
- return [ obj ];
- }
- // -------------------------- imagesLoaded -------------------------- //
- /**
- * @param {[Array, Element, NodeList, String]} elem
- * @param {[Object, Function]} options - if function, use as callback
- * @param {Function} onAlways - callback function
- * @returns {ImagesLoaded}
- */
- 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
- let queryElem = elem;
- if ( typeof elem == 'string' ) {
- queryElem = document.querySelectorAll( elem );
- }
- // bail if bad element
- if ( !queryElem ) {
- console.error(`Bad element for imagesLoaded ${queryElem || elem}`);
- return;
- }
- this.elements = makeArray( queryElem );
- this.options = {};
- // shift arguments if no options set
- if ( typeof options == 'function' ) {
- onAlways = options;
- } else {
- Object.assign( this.options, options );
- }
- if ( onAlways ) this.on( 'always', onAlways );
- this.getImages();
- // add jQuery Deferred object
- if ( $ ) this.jqDeferred = new $.Deferred();
- // HACK check async to allow time to bind listeners
- setTimeout( this.check.bind( this ) );
- }
- ImagesLoaded.prototype = Object.create( EvEmitter.prototype );
- ImagesLoaded.prototype.getImages = function() {
- this.images = [];
- // filter & find items if we have an item selector
- this.elements.forEach( this.addElementImages, this );
- };
- const elementNodeTypes = [ 1, 9, 11 ];
- /**
- * @param {Node} elem
- */
- 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
- let { nodeType } = elem;
- if ( !nodeType || !elementNodeTypes.includes( nodeType ) ) return;
- let childImgs = elem.querySelectorAll('img');
- // concat childElems to filterFound array
- for ( let img of childImgs ) {
- this.addImage( img );
- }
- // get child background images
- if ( typeof this.options.background == 'string' ) {
- let children = elem.querySelectorAll( this.options.background );
- for ( let child of children ) {
- this.addElementBackgroundImages( child );
- }
- }
- };
- const reURL = /url\((['"])?(.*?)\1\)/gi;
- ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
- let style = getComputedStyle( elem );
- // Firefox returns null if in a hidden iframe https://bugzil.la/548397
- if ( !style ) return;
- // get url inside url("...")
- let matches = reURL.exec( style.backgroundImage );
- while ( matches !== null ) {
- let url = matches && matches[2];
- if ( url ) {
- this.addBackground( url, elem );
- }
- matches = reURL.exec( style.backgroundImage );
- }
- };
- /**
- * @param {Image} img
- */
- ImagesLoaded.prototype.addImage = function( img ) {
- let loadingImage = new LoadingImage( img );
- this.images.push( loadingImage );
- };
- ImagesLoaded.prototype.addBackground = function( url, elem ) {
- let background = new Background( url, elem );
- this.images.push( background );
- };
- ImagesLoaded.prototype.check = function() {
- this.progressedCount = 0;
- this.hasAnyBroken = false;
- // complete if no images
- if ( !this.images.length ) {
- this.complete();
- return;
- }
- /* eslint-disable-next-line func-style */
- let onProgress = ( image, elem, message ) => {
- // HACK - Chrome triggers event before object properties have changed. #83
- setTimeout( () => {
- 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() {
- let eventName = this.hasAnyBroken ? 'fail' : 'done';
- this.isComplete = true;
- this.emitEvent( eventName, [ this ] );
- this.emitEvent( 'always', [ this ] );
- if ( this.jqDeferred ) {
- let 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.
- let 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();
- // add crossOrigin attribute. #204
- if ( this.img.crossOrigin ) {
- this.proxyImage.crossOrigin = this.img.crossOrigin;
- }
- 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.currentSrc || this.img.src;
- };
- LoadingImage.prototype.getIsImageComplete = function() {
- // check for non-zero, non-undefined naturalWidth
- // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671
- return this.img.complete && this.img.naturalWidth;
- };
- LoadingImage.prototype.confirm = function( isLoaded, message ) {
- this.isLoaded = isLoaded;
- let { parentNode } = this.img;
- // emit progress with parent <picture> or self <img>
- let elem = parentNode.nodeName === 'PICTURE' ? parentNode : this.img;
- this.emitEvent( 'progress', [ this, elem, message ] );
- };
- // ----- events ----- //
- // trigger specified handler for event type
- LoadingImage.prototype.handleEvent = function( event ) {
- let 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
- let 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, onAlways ) {
- let instance = new ImagesLoaded( this, options, onAlways );
- return instance.jqDeferred.promise( $( this ) );
- };
- };
- // try making plugin
- ImagesLoaded.makeJQueryPlugin();
- // -------------------------- -------------------------- //
- return ImagesLoaded;
- } );
|