You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
384 lines
10 KiB
384 lines
10 KiB
/*! |
|
* imagesLoaded v3.2.0 |
|
* 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( [ |
|
'eventEmitter/EventEmitter', |
|
'eventie/eventie' |
|
], function( EventEmitter, eventie ) { |
|
return factory( window, EventEmitter, eventie ); |
|
}); |
|
} else if ( typeof module == 'object' && module.exports ) { |
|
// CommonJS |
|
module.exports = factory( |
|
window, |
|
require('wolfy87-eventemitter'), |
|
require('eventie') |
|
); |
|
} else { |
|
// browser global |
|
window.imagesLoaded = factory( |
|
window, |
|
window.EventEmitter, |
|
window.eventie |
|
); |
|
} |
|
|
|
})( window, |
|
|
|
// -------------------------- factory -------------------------- // |
|
|
|
function factory( window, EventEmitter, eventie ) { |
|
|
|
'use strict'; |
|
|
|
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; |
|
} |
|
|
|
var objToString = Object.prototype.toString; |
|
function isArray( obj ) { |
|
return objToString.call( obj ) == '[object Array]'; |
|
} |
|
|
|
// turn element or nodeList into an array |
|
function makeArray( obj ) { |
|
var ary = []; |
|
if ( 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 |
|
var _this = this; |
|
setTimeout( function() { |
|
_this.check(); |
|
}); |
|
} |
|
|
|
ImagesLoaded.prototype = new EventEmitter(); |
|
|
|
ImagesLoaded.prototype.options = {}; |
|
|
|
ImagesLoaded.prototype.getImages = function() { |
|
this.images = []; |
|
|
|
// filter & find items if we have an item selector |
|
for ( var i=0; i < this.elements.length; i++ ) { |
|
var elem = this.elements[i]; |
|
this.addElementImages( elem ); |
|
} |
|
}; |
|
|
|
/** |
|
* @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 = getStyle( elem ); |
|
// get url inside url("...") |
|
var reURL = /url\(['"]*([^'"\)]+)['"]*\)/gi; |
|
var matches = reURL.exec( style.backgroundImage ); |
|
while ( matches !== null ) { |
|
var url = matches && matches[1]; |
|
if ( url ) { |
|
this.addBackground( url, elem ); |
|
} |
|
matches = reURL.exec( style.backgroundImage ); |
|
} |
|
}; |
|
|
|
// IE8 |
|
var getStyle = window.getComputedStyle || function( elem ) { |
|
return elem.currentStyle; |
|
}; |
|
|
|
/** |
|
* @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 ); |
|
}); |
|
} |
|
|
|
for ( var i=0; i < this.images.length; i++ ) { |
|
var loadingImage = this.images[i]; |
|
loadingImage.once( 'progress', onProgress ); |
|
loadingImage.check(); |
|
} |
|
}; |
|
|
|
ImagesLoaded.prototype.progress = function( image, elem, message ) { |
|
this.progressedCount++; |
|
this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; |
|
// progress event |
|
this.emit( '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.emit( eventName, this ); |
|
this.emit( 'always', this ); |
|
if ( this.jqDeferred ) { |
|
var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; |
|
this.jqDeferred[ jqMethod ]( this ); |
|
} |
|
}; |
|
|
|
// -------------------------- -------------------------- // |
|
|
|
function LoadingImage( img ) { |
|
this.img = img; |
|
} |
|
|
|
LoadingImage.prototype = new EventEmitter(); |
|
|
|
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(); |
|
eventie.bind( this.proxyImage, 'load', this ); |
|
eventie.bind( this.proxyImage, 'error', this ); |
|
// bind to image as well for Firefox. #191 |
|
eventie.bind( this.img, 'load', this ); |
|
eventie.bind( this.img, '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.emit( '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() { |
|
eventie.unbind( this.proxyImage, 'load', this ); |
|
eventie.unbind( this.proxyImage, 'error', this ); |
|
eventie.unbind( this.img, 'load', this ); |
|
eventie.unbind( this.img, 'error', this ); |
|
}; |
|
|
|
// -------------------------- Background -------------------------- // |
|
|
|
function Background( url, element ) { |
|
this.url = url; |
|
this.element = element; |
|
this.img = new Image(); |
|
} |
|
|
|
// inherit LoadingImage prototype |
|
Background.prototype = new LoadingImage(); |
|
|
|
Background.prototype.check = function() { |
|
eventie.bind( this.img, 'load', this ); |
|
eventie.bind( this.img, '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() { |
|
eventie.unbind( this.img, 'load', this ); |
|
eventie.unbind( this.img, 'error', this ); |
|
}; |
|
|
|
Background.prototype.confirm = function( isLoaded, message ) { |
|
this.isLoaded = isLoaded; |
|
this.emit( '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; |
|
|
|
});
|
|
|