-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
43a5473
commit 19e1048
Showing
51 changed files
with
37,755 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,335 @@ | ||
/*! | ||
* 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; | ||
|
||
} ); |
Oops, something went wrong.