imagesloaded.pkgd.js 12 KB

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