| 1 |
/*! |
| 2 |
* imagesLoaded PACKAGED v4.1.0 |
| 3 |
* JavaScript is all like "You images are done yet or what?" |
| 4 |
* MIT License |
| 5 |
*/ |
| 6 |
|
| 7 |
/** |
| 8 |
* EvEmitter v1.0.1 |
| 9 |
* Lil' event emitter |
| 10 |
* MIT License |
| 11 |
*/ |
| 12 |
|
| 13 |
/* jshint unused: true, undef: true, strict: true */ |
| 14 |
|
| 15 |
( function( global, factory ) { |
| 16 |
// universal module definition |
| 17 |
/* jshint strict: false */ /* globals define, module */ |
| 18 |
if ( typeof define == 'function' && define.amd ) { |
| 19 |
// AMD - RequireJS |
| 20 |
define( 'ev-emitter/ev-emitter',factory ); |
| 21 |
} else if ( typeof module == 'object' && module.exports ) { |
| 22 |
// CommonJS - Browserify, Webpack |
| 23 |
module.exports = factory(); |
| 24 |
} else { |
| 25 |
// Browser globals |
| 26 |
global.EvEmitter = factory(); |
| 27 |
} |
| 28 |
|
| 29 |
}( this, function() { |
| 30 |
|
| 31 |
|
| 32 |
|
| 33 |
function EvEmitter() {} |
| 34 |
|
| 35 |
var proto = EvEmitter.prototype; |
| 36 |
|
| 37 |
proto.on = function( eventName, listener ) { |
| 38 |
if ( !eventName || !listener ) { |
| 39 |
return; |
| 40 |
} |
| 41 |
// set events hash |
| 42 |
var events = this._events = this._events || {}; |
| 43 |
// set listeners array |
| 44 |
var listeners = events[ eventName ] = events[ eventName ] || []; |
| 45 |
// only add once |
| 46 |
if ( listeners.indexOf( listener ) == -1 ) { |
| 47 |
listeners.push( listener ); |
| 48 |
} |
| 49 |
|
| 50 |
return this; |
| 51 |
}; |
| 52 |
|
| 53 |
proto.once = function( eventName, listener ) { |
| 54 |
if ( !eventName || !listener ) { |
| 55 |
return; |
| 56 |
} |
| 57 |
// add event |
| 58 |
this.on( eventName, listener ); |
| 59 |
// set once flag |
| 60 |
// set onceEvents hash |
| 61 |
var onceEvents = this._onceEvents = this._onceEvents || {}; |
| 62 |
// set onceListeners array |
| 63 |
var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || []; |
| 64 |
// set flag |
| 65 |
onceListeners[ listener ] = true; |
| 66 |
|
| 67 |
return this; |
| 68 |
}; |
| 69 |
|
| 70 |
proto.off = function( eventName, listener ) { |
| 71 |
var listeners = this._events && this._events[ eventName ]; |
| 72 |
if ( !listeners || !listeners.length ) { |
| 73 |
return; |
| 74 |
} |
| 75 |
var index = listeners.indexOf( listener ); |
| 76 |
if ( index != -1 ) { |
| 77 |
listeners.splice( index, 1 ); |
| 78 |
} |
| 79 |
|
| 80 |
return this; |
| 81 |
}; |
| 82 |
|
| 83 |
proto.emitEvent = function( eventName, args ) { |
| 84 |
var listeners = this._events && this._events[ eventName ]; |
| 85 |
if ( !listeners || !listeners.length ) { |
| 86 |
return; |
| 87 |
} |
| 88 |
var i = 0; |
| 89 |
var listener = listeners[i]; |
| 90 |
args = args || []; |
| 91 |
// once stuff |
| 92 |
var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; |
| 93 |
|
| 94 |
while ( listener ) { |
| 95 |
var isOnce = onceListeners && onceListeners[ listener ]; |
| 96 |
if ( isOnce ) { |
| 97 |
// remove listener |
| 98 |
// remove before trigger to prevent recursion |
| 99 |
this.off( eventName, listener ); |
| 100 |
// unset once flag |
| 101 |
delete onceListeners[ listener ]; |
| 102 |
} |
| 103 |
// trigger listener |
| 104 |
listener.apply( this, args ); |
| 105 |
// get next listener |
| 106 |
i += isOnce ? 0 : 1; |
| 107 |
listener = listeners[i]; |
| 108 |
} |
| 109 |
|
| 110 |
return this; |
| 111 |
}; |
| 112 |
|
| 113 |
return EvEmitter; |
| 114 |
|
| 115 |
})); |
| 116 |
|
| 117 |
/*! |
| 118 |
* imagesLoaded v4.1.0 |
| 119 |
* JavaScript is all like "You images are done yet or what?" |
| 120 |
* MIT License |
| 121 |
*/ |
| 122 |
|
| 123 |
( function( window, factory ) { 'use strict'; |
| 124 |
// universal module definition |
| 125 |
|
| 126 |
/*global define: false, module: false, require: false */ |
| 127 |
|
| 128 |
if ( typeof define == 'function' && define.amd ) { |
| 129 |
// AMD |
| 130 |
define( [ |
| 131 |
'ev-emitter/ev-emitter' |
| 132 |
], function( EvEmitter ) { |
| 133 |
return factory( window, EvEmitter ); |
| 134 |
}); |
| 135 |
} else if ( typeof module == 'object' && module.exports ) { |
| 136 |
// CommonJS |
| 137 |
module.exports = factory( |
| 138 |
window, |
| 139 |
require('ev-emitter') |
| 140 |
); |
| 141 |
} else { |
| 142 |
// browser global |
| 143 |
window.imagesLoaded = factory( |
| 144 |
window, |
| 145 |
window.EvEmitter |
| 146 |
); |
| 147 |
} |
| 148 |
|
| 149 |
})( window, |
| 150 |
|
| 151 |
// -------------------------- factory -------------------------- // |
| 152 |
|
| 153 |
function factory( window, EvEmitter ) { |
| 154 |
|
| 155 |
|
| 156 |
|
| 157 |
var $ = window.jQuery; |
| 158 |
var console = window.console; |
| 159 |
|
| 160 |
// -------------------------- helpers -------------------------- // |
| 161 |
|
| 162 |
// extend objects |
| 163 |
function extend( a, b ) { |
| 164 |
for ( var prop in b ) { |
| 165 |
a[ prop ] = b[ prop ]; |
| 166 |
} |
| 167 |
return a; |
| 168 |
} |
| 169 |
|
| 170 |
// turn element or nodeList into an array |
| 171 |
function makeArray( obj ) { |
| 172 |
var ary = []; |
| 173 |
if ( Array.isArray( obj ) ) { |
| 174 |
// use object if already an array |
| 175 |
ary = obj; |
| 176 |
} else if ( typeof obj.length == 'number' ) { |
| 177 |
// convert nodeList to array |
| 178 |
for ( var i=0; i < obj.length; i++ ) { |
| 179 |
ary.push( obj[i] ); |
| 180 |
} |
| 181 |
} else { |
| 182 |
// array of single index |
| 183 |
ary.push( obj ); |
| 184 |
} |
| 185 |
return ary; |
| 186 |
} |
| 187 |
|
| 188 |
// -------------------------- imagesLoaded -------------------------- // |
| 189 |
|
| 190 |
/** |
| 191 |
* @param {Array, Element, NodeList, String} elem |
| 192 |
* @param {Object or Function} options - if function, use as callback |
| 193 |
* @param {Function} onAlways - callback function |
| 194 |
*/ |
| 195 |
function ImagesLoaded( elem, options, onAlways ) { |
| 196 |
// coerce ImagesLoaded() without new, to be new ImagesLoaded() |
| 197 |
if ( !( this instanceof ImagesLoaded ) ) { |
| 198 |
return new ImagesLoaded( elem, options, onAlways ); |
| 199 |
} |
| 200 |
// use elem as selector string |
| 201 |
if ( typeof elem == 'string' ) { |
| 202 |
elem = document.querySelectorAll( elem ); |
| 203 |
} |
| 204 |
|
| 205 |
this.elements = makeArray( elem ); |
| 206 |
this.options = extend( {}, this.options ); |
| 207 |
|
| 208 |
if ( typeof options == 'function' ) { |
| 209 |
onAlways = options; |
| 210 |
} else { |
| 211 |
extend( this.options, options ); |
| 212 |
} |
| 213 |
|
| 214 |
if ( onAlways ) { |
| 215 |
this.on( 'always', onAlways ); |
| 216 |
} |
| 217 |
|
| 218 |
this.getImages(); |
| 219 |
|
| 220 |
if ( $ ) { |
| 221 |
// add jQuery Deferred object |
| 222 |
this.jqDeferred = new $.Deferred(); |
| 223 |
} |
| 224 |
|
| 225 |
// HACK check async to allow time to bind listeners |
| 226 |
setTimeout( function() { |
| 227 |
this.check(); |
| 228 |
}.bind( this )); |
| 229 |
} |
| 230 |
|
| 231 |
ImagesLoaded.prototype = Object.create( EvEmitter.prototype ); |
| 232 |
|
| 233 |
ImagesLoaded.prototype.options = {}; |
| 234 |
|
| 235 |
ImagesLoaded.prototype.getImages = function() { |
| 236 |
this.images = []; |
| 237 |
|
| 238 |
// filter & find items if we have an item selector |
| 239 |
this.elements.forEach( this.addElementImages, this ); |
| 240 |
}; |
| 241 |
|
| 242 |
/** |
| 243 |
* @param {Node} element |
| 244 |
*/ |
| 245 |
ImagesLoaded.prototype.addElementImages = function( elem ) { |
| 246 |
// filter siblings |
| 247 |
if ( elem.nodeName == 'IMG' ) { |
| 248 |
this.addImage( elem ); |
| 249 |
} |
| 250 |
// get background image on element |
| 251 |
if ( this.options.background === true ) { |
| 252 |
this.addElementBackgroundImages( elem ); |
| 253 |
} |
| 254 |
|
| 255 |
// find children |
| 256 |
// no non-element nodes, #143 |
| 257 |
var nodeType = elem.nodeType; |
| 258 |
if ( !nodeType || !elementNodeTypes[ nodeType ] ) { |
| 259 |
return; |
| 260 |
} |
| 261 |
var childImgs = elem.querySelectorAll('img'); |
| 262 |
// concat childElems to filterFound array |
| 263 |
for ( var i=0; i < childImgs.length; i++ ) { |
| 264 |
var img = childImgs[i]; |
| 265 |
this.addImage( img ); |
| 266 |
} |
| 267 |
|
| 268 |
// get child background images |
| 269 |
if ( typeof this.options.background == 'string' ) { |
| 270 |
var children = elem.querySelectorAll( this.options.background ); |
| 271 |
for ( i=0; i < children.length; i++ ) { |
| 272 |
var child = children[i]; |
| 273 |
this.addElementBackgroundImages( child ); |
| 274 |
} |
| 275 |
} |
| 276 |
}; |
| 277 |
|
| 278 |
var elementNodeTypes = { |
| 279 |
1: true, |
| 280 |
9: true, |
| 281 |
11: true |
| 282 |
}; |
| 283 |
|
| 284 |
ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { |
| 285 |
var style = getComputedStyle( elem ); |
| 286 |
if ( !style ) { |
| 287 |
// Firefox returns null if in a hidden iframe https://bugzil.la/548397 |
| 288 |
return; |
| 289 |
} |
| 290 |
// get url inside url("...") |
| 291 |
var reURL = /url\((['"])?(.*?)\1\)/gi; |
| 292 |
var matches = reURL.exec( style.backgroundImage ); |
| 293 |
while ( matches !== null ) { |
| 294 |
var url = matches && matches[2]; |
| 295 |
if ( url ) { |
| 296 |
this.addBackground( url, elem ); |
| 297 |
} |
| 298 |
matches = reURL.exec( style.backgroundImage ); |
| 299 |
} |
| 300 |
}; |
| 301 |
|
| 302 |
/** |
| 303 |
* @param {Image} img |
| 304 |
*/ |
| 305 |
ImagesLoaded.prototype.addImage = function( img ) { |
| 306 |
var loadingImage = new LoadingImage( img ); |
| 307 |
this.images.push( loadingImage ); |
| 308 |
}; |
| 309 |
|
| 310 |
ImagesLoaded.prototype.addBackground = function( url, elem ) { |
| 311 |
var background = new Background( url, elem ); |
| 312 |
this.images.push( background ); |
| 313 |
}; |
| 314 |
|
| 315 |
ImagesLoaded.prototype.check = function() { |
| 316 |
var _this = this; |
| 317 |
this.progressedCount = 0; |
| 318 |
this.hasAnyBroken = false; |
| 319 |
// complete if no images |
| 320 |
if ( !this.images.length ) { |
| 321 |
this.complete(); |
| 322 |
return; |
| 323 |
} |
| 324 |
|
| 325 |
function onProgress( image, elem, message ) { |
| 326 |
// HACK - Chrome triggers event before object properties have changed. #83 |
| 327 |
setTimeout( function() { |
| 328 |
_this.progress( image, elem, message ); |
| 329 |
}); |
| 330 |
} |
| 331 |
|
| 332 |
this.images.forEach( function( loadingImage ) { |
| 333 |
loadingImage.once( 'progress', onProgress ); |
| 334 |
loadingImage.check(); |
| 335 |
}); |
| 336 |
}; |
| 337 |
|
| 338 |
ImagesLoaded.prototype.progress = function( image, elem, message ) { |
| 339 |
this.progressedCount++; |
| 340 |
this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; |
| 341 |
// progress event |
| 342 |
this.emitEvent( 'progress', [ this, image, elem ] ); |
| 343 |
if ( this.jqDeferred && this.jqDeferred.notify ) { |
| 344 |
this.jqDeferred.notify( this, image ); |
| 345 |
} |
| 346 |
// check if completed |
| 347 |
if ( this.progressedCount == this.images.length ) { |
| 348 |
this.complete(); |
| 349 |
} |
| 350 |
|
| 351 |
if ( this.options.debug && console ) { |
| 352 |
console.log( 'progress: ' + message, image, elem ); |
| 353 |
} |
| 354 |
}; |
| 355 |
|
| 356 |
ImagesLoaded.prototype.complete = function() { |
| 357 |
var eventName = this.hasAnyBroken ? 'fail' : 'done'; |
| 358 |
this.isComplete = true; |
| 359 |
this.emitEvent( eventName, [ this ] ); |
| 360 |
this.emitEvent( 'always', [ this ] ); |
| 361 |
if ( this.jqDeferred ) { |
| 362 |
var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; |
| 363 |
this.jqDeferred[ jqMethod ]( this ); |
| 364 |
} |
| 365 |
}; |
| 366 |
|
| 367 |
// -------------------------- -------------------------- // |
| 368 |
|
| 369 |
function LoadingImage( img ) { |
| 370 |
this.img = img; |
| 371 |
} |
| 372 |
|
| 373 |
LoadingImage.prototype = Object.create( EvEmitter.prototype ); |
| 374 |
|
| 375 |
LoadingImage.prototype.check = function() { |
| 376 |
// If complete is true and browser supports natural sizes, |
| 377 |
// try to check for image status manually. |
| 378 |
var isComplete = this.getIsImageComplete(); |
| 379 |
if ( isComplete ) { |
| 380 |
// report based on naturalWidth |
| 381 |
this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); |
| 382 |
return; |
| 383 |
} |
| 384 |
|
| 385 |
// If none of the checks above matched, simulate loading on detached element. |
| 386 |
this.proxyImage = new Image(); |
| 387 |
this.proxyImage.addEventListener( 'load', this ); |
| 388 |
this.proxyImage.addEventListener( 'error', this ); |
| 389 |
// bind to image as well for Firefox. #191 |
| 390 |
this.img.addEventListener( 'load', this ); |
| 391 |
this.img.addEventListener( 'error', this ); |
| 392 |
this.proxyImage.src = this.img.src; |
| 393 |
}; |
| 394 |
|
| 395 |
LoadingImage.prototype.getIsImageComplete = function() { |
| 396 |
return this.img.complete && this.img.naturalWidth !== undefined; |
| 397 |
}; |
| 398 |
|
| 399 |
LoadingImage.prototype.confirm = function( isLoaded, message ) { |
| 400 |
this.isLoaded = isLoaded; |
| 401 |
this.emitEvent( 'progress', [ this, this.img, message ] ); |
| 402 |
}; |
| 403 |
|
| 404 |
// ----- events ----- // |
| 405 |
|
| 406 |
// trigger specified handler for event type |
| 407 |
LoadingImage.prototype.handleEvent = function( event ) { |
| 408 |
var method = 'on' + event.type; |
| 409 |
if ( this[ method ] ) { |
| 410 |
this[ method ]( event ); |
| 411 |
} |
| 412 |
}; |
| 413 |
|
| 414 |
LoadingImage.prototype.onload = function() { |
| 415 |
this.confirm( true, 'onload' ); |
| 416 |
this.unbindEvents(); |
| 417 |
}; |
| 418 |
|
| 419 |
LoadingImage.prototype.onerror = function() { |
| 420 |
this.confirm( false, 'onerror' ); |
| 421 |
this.unbindEvents(); |
| 422 |
}; |
| 423 |
|
| 424 |
LoadingImage.prototype.unbindEvents = function() { |
| 425 |
this.proxyImage.removeEventListener( 'load', this ); |
| 426 |
this.proxyImage.removeEventListener( 'error', this ); |
| 427 |
this.img.removeEventListener( 'load', this ); |
| 428 |
this.img.removeEventListener( 'error', this ); |
| 429 |
}; |
| 430 |
|
| 431 |
// -------------------------- Background -------------------------- // |
| 432 |
|
| 433 |
function Background( url, element ) { |
| 434 |
this.url = url; |
| 435 |
this.element = element; |
| 436 |
this.img = new Image(); |
| 437 |
} |
| 438 |
|
| 439 |
// inherit LoadingImage prototype |
| 440 |
Background.prototype = Object.create( LoadingImage.prototype ); |
| 441 |
|
| 442 |
Background.prototype.check = function() { |
| 443 |
this.img.addEventListener( 'load', this ); |
| 444 |
this.img.addEventListener( 'error', this ); |
| 445 |
this.img.src = this.url; |
| 446 |
// check if image is already complete |
| 447 |
var isComplete = this.getIsImageComplete(); |
| 448 |
if ( isComplete ) { |
| 449 |
this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); |
| 450 |
this.unbindEvents(); |
| 451 |
} |
| 452 |
}; |
| 453 |
|
| 454 |
Background.prototype.unbindEvents = function() { |
| 455 |
this.img.removeEventListener( 'load', this ); |
| 456 |
this.img.removeEventListener( 'error', this ); |
| 457 |
}; |
| 458 |
|
| 459 |
Background.prototype.confirm = function( isLoaded, message ) { |
| 460 |
this.isLoaded = isLoaded; |
| 461 |
this.emitEvent( 'progress', [ this, this.element, message ] ); |
| 462 |
}; |
| 463 |
|
| 464 |
// -------------------------- jQuery -------------------------- // |
| 465 |
|
| 466 |
ImagesLoaded.makeJQueryPlugin = function( jQuery ) { |
| 467 |
jQuery = jQuery || window.jQuery; |
| 468 |
if ( !jQuery ) { |
| 469 |
return; |
| 470 |
} |
| 471 |
// set local variable |
| 472 |
$ = jQuery; |
| 473 |
// $().imagesLoaded() |
| 474 |
$.fn.imagesLoaded = function( options, callback ) { |
| 475 |
var instance = new ImagesLoaded( this, options, callback ); |
| 476 |
return instance.jqDeferred.promise( $(this) ); |
| 477 |
}; |
| 478 |
}; |
| 479 |
// try making plugin |
| 480 |
ImagesLoaded.makeJQueryPlugin(); |
| 481 |
|
| 482 |
// -------------------------- -------------------------- // |
| 483 |
|
| 484 |
return ImagesLoaded; |
| 485 |
|
| 486 |
}); |
| 487 |
|
| 488 |
|