1/*! 2 * imagesLoaded v3.0.2 3 * JavaScript is all like "You images are done yet or what?" 4 */ 5 6( function( window ) { 7 8'use strict'; 9 10var $ = window.jQuery; 11var console = window.console; 12var hasConsole = typeof console !== 'undefined'; 13 14// -------------------------- helpers -------------------------- // 15 16// extend objects 17function extend( a, b ) { 18 for ( var prop in b ) { 19 a[ prop ] = b[ prop ]; 20 } 21 return a; 22} 23 24var objToString = Object.prototype.toString; 25function isArray( obj ) { 26 return objToString.call( obj ) === '[object Array]'; 27} 28 29// turn element or nodeList into an array 30function makeArray( obj ) { 31 var ary = []; 32 if ( isArray( obj ) ) { 33 // use object if already an array 34 ary = obj; 35 } else if ( typeof obj.length === 'number' ) { 36 // convert nodeList to array 37 for ( var i=0, len = obj.length; i < len; i++ ) { 38 ary.push( obj[i] ); 39 } 40 } else { 41 // array of single index 42 ary.push( obj ); 43 } 44 return ary; 45} 46 47// -------------------------- -------------------------- // 48 49function defineImagesLoaded( EventEmitter, eventie ) { 50 51 /** 52 * @param {Array, Element, NodeList, String} elem 53 * @param {Object or Function} options - if function, use as callback 54 * @param {Function} onAlways - callback function 55 */ 56 function ImagesLoaded( elem, options, onAlways ) { 57 // coerce ImagesLoaded() without new, to be new ImagesLoaded() 58 if ( !( this instanceof ImagesLoaded ) ) { 59 return new ImagesLoaded( elem, options ); 60 } 61 // use elem as selector string 62 if ( typeof elem === 'string' ) { 63 elem = document.querySelectorAll( elem ); 64 } 65 66 this.elements = makeArray( elem ); 67 this.options = extend( {}, this.options ); 68 69 if ( typeof options === 'function' ) { 70 onAlways = options; 71 } else { 72 extend( this.options, options ); 73 } 74 75 if ( onAlways ) { 76 this.on( 'always', onAlways ); 77 } 78 79 this.getImages(); 80 81 if ( $ ) { 82 // add jQuery Deferred object 83 this.jqDeferred = new $.Deferred(); 84 } 85 86 // HACK check async to allow time to bind listeners 87 var _this = this; 88 setTimeout( function() { 89 _this.check(); 90 }); 91 } 92 93 ImagesLoaded.prototype = new EventEmitter(); 94 95 ImagesLoaded.prototype.options = {}; 96 97 ImagesLoaded.prototype.getImages = function() { 98 this.images = []; 99 100 // filter & find items if we have an item selector 101 for ( var i=0, len = this.elements.length; i < len; i++ ) { 102 var elem = this.elements[i]; 103 // filter siblings 104 if ( elem.nodeName === 'IMG' ) { 105 this.addImage( elem ); 106 } 107 // find children 108 var childElems = elem.querySelectorAll('img'); 109 // concat childElems to filterFound array 110 for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { 111 var img = childElems[j]; 112 this.addImage( img ); 113 } 114 } 115 }; 116 117 /** 118 * @param {Image} img 119 */ 120 ImagesLoaded.prototype.addImage = function( img ) { 121 var loadingImage = new LoadingImage( img ); 122 this.images.push( loadingImage ); 123 }; 124 125 ImagesLoaded.prototype.check = function() { 126 var _this = this; 127 var checkedCount = 0; 128 var length = this.images.length; 129 this.hasAnyBroken = false; 130 // complete if no images 131 if ( !length ) { 132 this.complete(); 133 return; 134 } 135 136 function onConfirm( image, message ) { 137 if ( _this.options.debug && hasConsole ) { 138 console.log( 'confirm', image, message ); 139 } 140 141 _this.progress( image ); 142 checkedCount++; 143 if ( checkedCount === length ) { 144 _this.complete(); 145 } 146 return true; // bind once 147 } 148 149 for ( var i=0; i < length; i++ ) { 150 var loadingImage = this.images[i]; 151 loadingImage.on( 'confirm', onConfirm ); 152 loadingImage.check(); 153 } 154 }; 155 156 ImagesLoaded.prototype.progress = function( image ) { 157 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 158 this.emit( 'progress', this, image ); 159 if ( this.jqDeferred ) { 160 this.jqDeferred.notify( this, image ); 161 } 162 }; 163 164 ImagesLoaded.prototype.complete = function() { 165 var eventName = this.hasAnyBroken ? 'fail' : 'done'; 166 this.isComplete = true;
167 this.emit( eventName, this ); 168 this.emit( 'always', this ); 169 if ( this.jqDeferred ) { 170 var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; 171 this.jqDeferred[ jqMethod ]( this ); 172 } 173 }; 174 175 // -------------------------- jquery -------------------------- // 176 177 if ( $ ) { 178 $.fn.imagesLoaded = function( options, callback ) { 179 var instance = new ImagesLoaded( this, options, callback ); 180 return instance.jqDeferred.promise( $(this) ); 181 }; 182 } 183 184 185 // -------------------------- -------------------------- // 186 187 var cache = {}; 188 189 function LoadingImage( img ) { 190 this.img = img; 191 } 192 193 LoadingImage.prototype = new EventEmitter(); 194 195 LoadingImage.prototype.check = function() { 196 // first check cached any previous images that have same src 197 var cached = cache[ this.img.src ]; 198 if ( cached ) { 199 this.useCached( cached ); 200 return; 201 } 202 // add this to cache 203 cache[ this.img.src ] = this; 204 205 // If complete is true and browser supports natural sizes, 206 // try to check for image status manually. 207 if ( this.img.complete && this.img.naturalWidth !== undefined ) { 208 // report based on naturalWidth 209 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 210 return; 211 } 212 213 // If none of the checks above matched, simulate loading on detached element. 214 var proxyImage = this.proxyImage = new Image(); 215 eventie.bind( proxyImage, 'load', this ); 216 eventie.bind( proxyImage, 'error', this ); 217 proxyImage.src = this.img.src; 218 }; 219 220 LoadingImage.prototype.useCached = function( cached ) { 221 if ( cached.isConfirmed ) { 222 this.confirm( cached.isLoaded, 'cached was confirmed' ); 223 } else { 224 var _this = this; 225 cached.on( 'confirm', function( image ) { 226 _this.confirm( image.isLoaded, 'cache emitted confirmed' ); 227 return true; // bind once 228 }); 229 } 230 }; 231 232 LoadingImage.prototype.confirm = function( isLoaded, message ) { 233 this.isConfirmed = true; 234 this.isLoaded = isLoaded; 235 this.emit( 'confirm', this, message ); 236 }; 237 238 // trigger specified handler for event type 239 LoadingImage.prototype.handleEvent = function( event ) { 240 var method = 'on' + event.type; 241 if ( this[ method ] ) { 242 this[ method ]( event ); 243 } 244 }; 245 246 LoadingImage.prototype.onload = function() { 247 this.confirm( true, 'onload' ); 248 this.unbindProxyEvents(); 249 }; 250 251 LoadingImage.prototype.onerror = function() { 252 this.confirm( false, 'onerror' ); 253 this.unbindProxyEvents(); 254 }; 255 256 LoadingImage.prototype.unbindProxyEvents = function() { 257 eventie.unbind( this.proxyImage, 'load', this ); 258 eventie.unbind( this.proxyImage, 'error', this ); 259 }; 260 261 // ----- ----- // 262 263 return ImagesLoaded; 264} 265 266// -------------------------- transport -------------------------- // 267 268if ( typeof define === 'function' && define.amd ) { 269 // AMD 270 define( [ 271 'eventEmitter', 272 'eventie' 273 ], 274 defineImagesLoaded ); 275} else { 276 // browser global 277 window.imagesLoaded = defineImagesLoaded( 278 window.EventEmitter, 279 window.eventie 280 ); 281} 282 283})( window );
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.