balmet.com

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs

imagesloaded.js (12283B)


      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