JAVASCRIPT   29

imagesLoaded

Guest on 13th July 2022 06:49:36 AM

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

Raw Paste


Login or Register to edit or fork this paste. It's free.