1/*! 2 * imagesLoaded PACKAGED v3.1.8 3 * JavaScript is all like "You images are done yet or what?" 4 * MIT License 5 */ 6 7/*! 8 * EventEmitter v4.2.6 - git.io/ee 9 * Oliver Caldwell 10 * MIT license 11 * @preserve 12 */ 13 14( function () { 15 16 /** 17 * Class for managing events. 18 * Can be extended to provide event functionality in other classes. 19 * 20 * @class EventEmitter Manages event registering and emitting. 21 */ 22 function EventEmitter() {} 23 24 // Shortcuts to improve speed and size 25 var proto = EventEmitter.prototype; 26 var exports = this; 27 var originalGlobalValue = exports.EventEmitter; 28 29 /** 30 * Finds the index of the listener for the event in it's storage array. 31 * 32 * @param {Function[]} listeners Array of listeners to search through. 33 * @param {Function} listener Method to look for. 34 * @return {Number} Index of the specified listener, -1 if not found 35 * @api private 36 */ 37 function indexOfListener( listeners, listener ) { 38 var i = listeners.length; 39 while ( i-- ) { 40 if ( listeners[i].listener === listener ) { 41 return i; 42 } 43 } 44 45 return -1; 46 } 47 48 /** 49 * Alias a method while keeping the context correct, to allow for overwriting of target method. 50 * 51 * @param {String} name The name of the target method. 52 * @return {Function} The aliased method 53 * @api private 54 */ 55 function alias( name ) { 56 return function aliasClosure() { 57 return this[ name ].apply( this, arguments ); 58 }; 59 } 60 61 /** 62 * Returns the listener array for the specified event. 63 * Will initialise the event object and listener arrays if required. 64 * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object c
vendor: 10,956 bytes, lines 64-357
64ontaining bar and baz. But only if you have either defined them with defineEvent or added some listeners to them. 65 * Each property in the object response is an array of listener functions. 66 * 67 * @param {String|RegExp} evt Name of the event to return the listeners from. 68 * @return {Function[]|Object} All listener functions for the event. 69 */ 70 proto.getListeners = function getListeners( evt ) { 71 var events = this._getEvents(); 72 var response; 73 var key; 74 75 // Return a concatenated array of all matching events if 76 // the selector is a regular expression. 77 if ( typeof evt === 'object' ) { 78 response = {}; 79 for ( key in events ) { 80 if ( events.hasOwnProperty( key ) && evt.test( key ) ) { 81 response[ key ] = events[ key ]; 82 } 83 } 84 } 85 else { 86 response = events[ evt ] || ( events[ evt ] = [] ); 87 } 88 89 return response; 90 }; 91 92 /** 93 * Takes a list of listener objects and flattens it into a list of listener functions. 94 * 95 * @param {Object[]} listeners Raw listener objects. 96 * @return {Function[]} Just the listener functions. 97 */ 98 proto.flattenListeners = function flattenListeners( listeners ) { 99 var flatListeners = []; 100 var i; 101 102 for ( i = 0; i < listeners.length; i += 1 ) { 103 flatListeners.push( listeners[i].listener ); 104 } 105 106 return flatListeners; 107 }; 108 109 /** 110 * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful. 111 * 112 * @param {String|RegExp} evt Name of the event to return the listeners from. 113 * @return {Object} All listener functions for an event in an object. 114 */ 115 proto.getListenersAsObject = function getListenersAsObject( evt ) { 116 var listeners = this.getListeners( evt ); 117 var response; 118 119 if ( listeners instanceof Array ) { 120 response = {}; 121 response[ evt ] = listeners; 122 } 123 124 return response || listeners; 125 }; 126 127 /** 128 * Adds a listener function to the specified event. 129 * The listener will not be added if it is a duplicate. 130 * If the listener returns true then it will be removed after it is called. 131 * If you pass a regular expression as the event name then the listener will be added to all events that match it. 132 * 133 * @param {String|RegExp} evt Name of the event to attach the listener to. 134 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 135 * @return {Object} Current instance of EventEmitter for chaining. 136 */ 137 proto.addListener = function addListener( evt, listener ) { 138 var listeners = this.getListenersAsObject( evt ); 139 var listenerIsWrapped = typeof listener === 'object'; 140 var key; 141 142 for ( key in listeners ) { 143 if ( listeners.hasOwnProperty( key ) && indexOfListener( listeners[ key ], listener ) === -1 ) { 144 listeners[ key ].push( listenerIsWrapped ? listener : { 145 listener: listener, 146 once: false 147 } ); 148 } 149 } 150 151 return this; 152 }; 153 154 /** 155 * Alias of addListener 156 */ 157 proto.on = alias( 'addListener' ); 158 159 /** 160 * Semi-alias of addListener. It will add a listener that will be 161 * automatically removed after it's first execution. 162 * 163 * @param {String|RegExp} evt Name of the event to attach the listener to. 164 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 165 * @return {Object} Current instance of EventEmitter for chaining. 166 */ 167 proto.addOnceListener = function addOnceListener( evt, listener ) { 168 return this.addListener( evt, { 169 listener: listener, 170 once: true 171 } ); 172 }; 173 174 /** 175 * Alias of addOnceListener. 176 */ 177 proto.once = alias( 'addOnceListener' ); 178 179 /** 180 * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad. 181 * You need to tell it what event names should be matched by a regex. 182 * 183 * @param {String} evt Name of the event to create. 184 * @return {Object} Current instance of EventEmitter for chaining. 185 */ 186 proto.defineEvent = function defineEvent( evt ) { 187 this.getListeners( evt ); 188 return this; 189 }; 190 191 /** 192 * Uses defineEvent to define multiple events. 193 * 194 * @param {String[]} evts An array of event names to define. 195 * @return {Object} Current instance of EventEmitter for chaining. 196 */ 197 proto.defineEvents = function defineEvents( evts ) { 198 for ( var i = 0; i < evts.length; i += 1 ) { 199 this.defineEvent( evts[i] ); 200 } 201 return this; 202 }; 203 204 /** 205 * Removes a listener function from the specified event. 206 * When passed a regular expression as the event name, it will remove the listener from all events that match it. 207 * 208 * @param {String|RegExp} evt Name of the event to remove the listener from. 209 * @param {Function} listener Method to remove from the event. 210 * @return {Object} Current instance of EventEmitter for chaining. 211 */ 212 proto.removeListener = function removeListener( evt, listener ) { 213 var listeners = this.getListenersAsObject( evt ); 214 var index; 215 var key; 216 217 for ( key in listeners ) { 218 if ( listeners.hasOwnProperty( key ) ) { 219 index = indexOfListener( listeners[ key ], listener ); 220 221 if ( index !== -1 ) { 222 listeners[ key ].splice( index, 1 ); 223 } 224 } 225 } 226 227 return this; 228 }; 229 230 /** 231 * Alias of removeListener 232 */ 233 proto.off = alias( 'removeListener' ); 234 235 /** 236 * Adds listeners in bulk using the manipulateListeners method. 237 * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added. 238 * You can also pass it a regular expression to add the array of listeners to all events that match it. 239 * Yeah, this function does quite a bit. That's probably a bad thing. 240 * 241 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once. 242 * @param {Function[]} [listeners] An optional array of listener functions to add. 243 * @return {Object} Current instance of EventEmitter for chaining. 244 */ 245 proto.addListeners = function addListeners( evt, listeners ) { 246 // Pass through to manipulateListeners 247 return this.manipulateListeners( false, evt, listeners ); 248 }; 249 250 /** 251 * Removes listeners in bulk using the manipulateListeners method. 252 * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. 253 * You can also pass it an event name and an array of listeners to be removed. 254 * You can also pass it a regular expression to remove the listeners from all events that match it. 255 * 256 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once. 257 * @param {Function[]} [listeners] An optional array of listener functions to remove. 258 * @return {Object} Current instance of EventEmitter for chaining. 259 */ 260 proto.removeListeners = function removeListeners( evt, listeners ) { 261 // Pass through to manipulateListeners 262 return this.manipulateListeners( true, evt, listeners ); 263 }; 264 265 /** 266 * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level. 267 * The first argument will determine if the listeners are removed (true) or added (false). 268 * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. 269 * You can also pass it an event name and an array of listeners to be added/removed. 270 * You can also pass it a regular expression to manipulate the listeners of all events that match it. 271 * 272 * @param {Boolean} remove True if you want to remove listeners, false if you want to add. 273 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once. 274 * @param {Function[]} [listeners] An optional array of listener functions to add/remove. 275 * @return {Object} Current instance of EventEmitter for chaining. 276 */ 277 proto.manipulateListeners = function manipulateListeners( remove, evt, listeners ) { 278 var i; 279 var value; 280 var single = remove ? this.removeListener : this.addListener; 281 var multiple = remove ? this.removeListeners : this.addListeners; 282 283 // If evt is an object then pass each of it's properties to this method 284 if ( typeof evt === 'object' && !( evt instanceof RegExp ) ) { 285 for ( i in evt ) { 286 if ( evt.hasOwnProperty(i) && ( value = evt[i] ) ) { 287 // Pass the single listener straight through to the singular method 288 if ( typeof value === 'function' ) { 289 single.call( this, i, value ); 290 } 291 else { 292 // Otherwise pass back to the multiple function 293 multiple.call( this, i, value ); 294 } 295 } 296 } 297 } 298 else { 299 // So evt must be a string 300 // And listeners must be an array of listeners 301 // Loop over it and pass each one to the multiple method 302 i = listeners.length; 303 while (i--) { 304 single.call( this, evt, listeners[i] ); 305 } 306 } 307 308 return this; 309 }; 310 311 /** 312 * Removes all listeners from a specified event. 313 * If you do not specify an event then all listeners will be removed. 314 * That means every event will be emptied. 315 * You can also pass a regex to remove all events that match it. 316 * 317 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed. 318 * @return {Object} Current instance of EventEmitter for chaining. 319 */ 320 proto.removeEvent = function removeEvent( evt ) { 321 var type = typeof evt; 322 var events = this._getEvents(); 323 var key; 324 325 // Remove different things depending on the state of evt 326 if ( type === 'string' ) { 327 // Remove all listeners for the specified event 328 delete events[ evt ]; 329 } 330 else if ( type === 'object' ) { 331 // Remove all events matching the regex. 332 for ( key in events ) { 333 if ( events.hasOwnProperty( key ) && evt.test( key ) ) { 334 delete events[ key ]; 335 } 336 } 337 } 338 else { 339 // Remove all listeners in all events 340 delete this._events; 341 } 342 343 return this; 344 }; 345 346 /** 347 * Alias of removeEvent. 348 * 349 * Added to mirror the node API. 350 */ 351 proto.removeAllListeners = alias( 'removeEvent' ); 352 353 /** 354 * Emits an event of your choice. 355 * When emitted, every listener attached to that event will be executed. 356 * If you pass the optional argument array then those arguments will be passed to every listener upon execution. 357 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
358 * So they will not arrive within the array on the other side, they will be separate. 359 * You can also pass a regular expression to emit to all events that match it. 360 * 361 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 362 * @param {Array} [args] Optional array of arguments to be passed to each listener. 363 * @return {Object} Current instance of EventEmitter for chaining. 364 */ 365 proto.emitEvent = function emitEvent( evt, args ) { 366 var listeners = this.getListenersAsObject( evt ); 367 var listener; 368 var i; 369 var key; 370 var response; 371 372 for ( key in listeners ) { 373 if ( listeners.hasOwnProperty( key ) ) { 374 i = listeners[ key ].length; 375 376 while (i--) { 377 // If the listener returns true then it shall be removed from the event 378 // The function is executed either with a basic call or an apply if there is an args array 379 listener = listeners[ key ][i]; 380 381 if ( listener.once === true ) { 382 this.removeListener( evt, listener.listener ); 383 } 384 385 response = listener.listener.apply( this, args || [] ); 386 387 if ( response === this._getOnceReturnValue() ) { 388 this.removeListener( evt, listener.listener ); 389 } 390 } 391 } 392 } 393 394 return this; 395 }; 396 397 /** 398 * Alias of emitEvent 399 */ 400 proto.trigger = alias( 'emitEvent' ); 401 402 /** 403 * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on. 404 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it. 405 * 406 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 407 * @param {...*} Optional additional arguments to be passed to each listener. 408 * @return {Object} Current instance of EventEmitter for chaining. 409 */ 410 proto.emit = function emit( evt ) { 411 var args = Array.prototype.slice.call( arguments, 1 ); 412 return this.emitEvent( evt, args ); 413 }; 414 415 /** 416 * Sets the current value to check against when executing listeners. If a 417 * listeners return value matches the one set here then it will be removed 418 * after execution. This value defaults to true. 419 * 420 * @param {*} value The new value to check for when executing listeners. 421 * @return {Object} Current instance of EventEmitter for chaining. 422 */ 423 proto.setOnceReturnValue = function setOnceReturnValue( value ) { 424 this._onceReturnValue = value; 425 return this; 426 }; 427 428 /** 429 * Fetches the current value to check against when executing listeners. If 430 * the listeners return value matches this one then it should be removed 431 * automatically. It will return true by default. 432 * 433 * @return {*|Boolean} The current value to check for or the default, true. 434 * @api private 435 */ 436 proto._getOnceReturnValue = function _getOnceReturnValue() { 437 if ( this.hasOwnProperty( '_onceReturnValue' ) ) { 438 return this._onceReturnValue; 439 } 440 else { 441 return true; 442 } 443 }; 444 445 /** 446 * Fetches the events object and creates one if required. 447 * 448 * @return {Object} The events storage object. 449 * @api private 450 */ 451 proto._getEvents = function _getEvents() { 452 return this._events || ( this._events = {} ); 453 }; 454 455 /** 456 * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version. 457 * 458 * @return {Function} Non conflicting EventEmitter class. 459 */ 460 EventEmitter.noConflict = function noConflict() { 461 exports.EventEmitter = originalGlobalValue; 462 return EventEmitter; 463 }; 464 465 // Expose the class either via AMD, CommonJS or the global object 466 if ( typeof define === 'function' && define.amd ) { 467 define( 'eventEmitter/EventEmitter', [], function () { 468 return EventEmitter; 469 } ); 470 } 471 else if ( typeof module === 'object' && module.exports ) { 472 module.exports = EventEmitter; 473 } 474 else { 475 this.EventEmitter = EventEmitter; 476 } 477}.call( this ) ); 478 479/*! 480 * eventie v1.0.4 481 * event binding helper 482 * eventie.bind( elem, 'click', myFn ) 483 * eventie.unbind( elem, 'click', myFn ) 484 */ 485 486/*jshint browser: true, undef: true, unused: true */ 487/*global define: false */ 488 489( function( window ) { 490 491 var docElem = document.documentElement; 492 493 var bind = function() {}; 494 495 function getIEEvent( obj ) { 496 var event = window.event; 497 // add event.target 498 event.target = event.target || event.srcElement || obj; 499 return event; 500 } 501 502 if ( docElem.addEventListener ) { 503 bind = function( obj, type, fn ) { 504 obj.addEventListener( type, fn, false ); 505 }; 506 } else if ( docElem.attachEvent ) { 507 bind = function( obj, type, fn ) { 508 obj[ type + fn ] = fn.handleEvent ? 509 function() { 510 var event = getIEEvent( obj ); 511 fn.handleEvent.call( fn, event ); 512 } : 513 function() { 514 var event = getIEEvent( obj ); 515 fn.call( obj, event ); 516 }; 517 obj.attachEvent( "on" + type, obj[ type + fn ] ); 518 }; 519 } 520 521 var unbind = function() {}; 522 523 if ( docElem.removeEventListener ) { 524 unbind = function( obj, type, fn ) { 525 obj.removeEventListener( type, fn, false ); 526 }; 527 } else if ( docElem.detachEvent ) { 528 unbind = function( obj, type, fn ) { 529 obj.detachEvent( "on" + type, obj[ type + fn ] ); 530 try { 531 delete obj[ type + fn ]; 532 } catch ( err ) { 533 // can't delete window object properties 534 obj[ type + fn ] = undefined; 535 } 536 }; 537 } 538 539 var eventie = { 540 bind: bind, 541 unbind: unbind 542 }; 543 544 // transport 545 if ( typeof define === 'function' && define.amd ) { 546 // AMD 547 define( 'eventie/eventie',eventie ); 548 } else { 549 // browser global 550 window.eventie = eventie; 551 } 552 553} )( this ); 554 555/*! 556 * imagesLoaded v3.1.8 557 * JavaScript is all like "You images are done yet or what?" 558 * MIT License 559 */ 560 561( function( window, factory ) { 562 // universal module definition 563 564 /*global define: false, module: false, require: false */ 565 566 if ( typeof define === 'function' && define.amd ) { 567 // AMD 568 define( [ 569 'eventEmitter/EventEmitter', 570 'eventie/eventie' 571 ], function( EventEmitter, eventie ) { 572 return factory( window, EventEmitter, eventie ); 573 }); 574 } else if ( typeof exports === 'object' ) { 575 // CommonJS 576 module.exports = factory( 577 window, 578 require('wolfy87-eventemitter'), 579 require('eventie') 580 ); 581 } else { 582 // browser global 583 window.imagesLoaded = factory( 584 window, 585 window.EventEmitter, 586 window.eventie 587 ); 588 } 589 590})( window, 591 592 // -------------------------- factory -------------------------- // 593 594 function factory( window, EventEmitter, eventie ) { 595 596 var $ = window.jQuery; 597 var console = window.console; 598 var hasConsole = typeof console !== 'undefined'; 599 600 // -------------------------- helpers -------------------------- // 601 602 // extend objects 603 function extend( a, b ) { 604 for ( var prop in b ) { 605 a[ prop ] = b[ prop ]; 606 } 607 return a; 608 } 609 610 var objToString = Object.prototype.toString; 611 function isArray( obj ) { 612 return objToString.call( obj ) === '[object Array]'; 613 } 614 615 // turn element or nodeList into an array 616 function makeArray( obj ) { 617 var ary = []; 618 if ( isArray( obj ) ) { 619 // use object if already an array 620 ary = obj; 621 } else if ( typeof obj.length === 'number' ) { 622 // convert nodeList to array 623 for ( var i=0, len = obj.length; i < len; i++ ) { 624 ary.push( obj[i] ); 625 } 626 } else { 627 // array of single index 628 ary.push( obj ); 629 } 630 return ary; 631 } 632 633 // -------------------------- imagesLoaded -------------------------- // 634 635 /** 636 * @param {Array, Element, NodeList, String} elem 637 * @param {Object or Function} options - if function, use as callback 638 * @param {Function} onAlways - callback function 639 */ 640 function ImagesLoaded( elem, options, onAlways ) { 641 // coerce ImagesLoaded() without new, to be new ImagesLoaded() 642 if ( !( this instanceof ImagesLoaded ) ) { 643 return new ImagesLoaded( elem, options ); 644 } 645 // use elem as selector string 646 if ( typeof elem === 'string' ) { 647 elem = document.querySelectorAll( elem ); 648 } 649 650 this.elements = makeArray( elem ); 651 this.options = extend( {}, this.options ); 652 653 if ( typeof options === 'function' ) { 654 onAlways = options; 655 } else { 656 extend( this.options, options ); 657 } 658 659 if ( onAlways ) { 660 this.on( 'always', onAlways ); 661 } 662 663 this.getImages(); 664 665 if ( $ ) { 666 // add jQuery Deferred object 667 this.jqDeferred = new $.Deferred(); 668 } 669 670 // HACK check async to allow time to bind listeners 671 var _this = this; 672 setTimeout( function() { 673 _this.check(); 674 }); 675 } 676 677 ImagesLoaded.prototype = new EventEmitter(); 678 679 ImagesLoaded.prototype.options = {}; 680 681 ImagesLoaded.prototype.getImages = function() { 682 this.images = []; 683 684 // filter & find items if we have an item selector 685 for ( var i=0, len = this.elements.length; i < len; i++ ) { 686 var elem = this.elements[i]; 687 // filter siblings 688 if ( elem.nodeName === 'IMG' ) { 689 this.addImage( elem ); 690 } 691 // find children 692 // no non-element nodes, #143 693 var nodeType = elem.nodeType; 694 if ( !nodeType || !( nodeType === 1 || nodeType === 9 || nodeType === 11 ) ) { 695 continue; 696 } 697 var childElems = elem.querySelectorAll('img'); 698 // concat childElems to filterFound array 699 for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { 700 var img = childElems[j]; 701 this.addImage( img ); 702 } 703 } 704 }; 705 706 /** 707 * @param {Image} img 708 */ 709 ImagesLoaded.prototype.addImage = function( img ) { 710 var loadingImage = new LoadingImage( img ); 711 this.images.push( loadingImage ); 712 }; 713
714 ImagesLoaded.prototype.check = function() { 715 var _this = this; 716 var checkedCount = 0; 717 var length = this.images.length; 718 this.hasAnyBroken = false; 719 // complete if no images 720 if ( !length ) { 721 this.complete(); 722 return; 723 } 724 725 function onConfirm( image, message ) { 726 if ( _this.options.debug && hasConsole ) { 727 console.log( 'confirm', image, message ); 728 } 729 730 _this.progress( image ); 731 checkedCount++; 732 if ( checkedCount === length ) { 733 _this.complete(); 734 } 735 return true; // bind once 736 } 737 738 for ( var i=0; i < length; i++ ) { 739 var loadingImage = this.images[i]; 740 loadingImage.on( 'confirm', onConfirm ); 741 loadingImage.check(); 742 } 743 }; 744 745 ImagesLoaded.prototype.progress = function( image ) { 746 this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; 747 // HACK - Chrome triggers event before object properties have changed. #83 748 var _this = this; 749 setTimeout( function() { 750 _this.emit( 'progress', _this, image ); 751 if ( _this.jqDeferred && _this.jqDeferred.notify ) { 752 _this.jqDeferred.notify( _this, image ); 753 } 754 } ); 755 }; 756 757 ImagesLoaded.prototype.complete = function() { 758 var eventName = this.hasAnyBroken ? 'fail' : 'done'; 759 this.isComplete = true; 760 var _this = this; 761 // HACK - another setTimeout so that confirm happens after progress 762 setTimeout( function() { 763 _this.emit( eventName, _this ); 764 _this.emit( 'always', _this ); 765 if ( _this.jqDeferred ) { 766 var jqMethod = _this.hasAnyBroken ? 'reject' : 'resolve'; 767 _this.jqDeferred[ jqMethod ]( _this ); 768 } 769 } ); 770 }; 771 772 // -------------------------- jquery -------------------------- // 773 774 if ( $ ) { 775 $.fn.imagesLoaded = function( options, callback ) { 776 var instance = new ImagesLoaded( this, options, callback ); 777 return instance.jqDeferred.promise( $(this) ); 778 }; 779 } 780 781 782 // -------------------------- -------------------------- // 783 784 function LoadingImage( img ) { 785 this.img = img; 786 } 787 788 LoadingImage.prototype = new EventEmitter(); 789 790 LoadingImage.prototype.check = function() { 791 // first check cached any previous images that have same src 792 var resource = cache[ this.img.src ] || new Resource( this.img.src ); 793 if ( resource.isConfirmed ) { 794 this.confirm( resource.isLoaded, 'cached was confirmed' ); 795 return; 796 } 797 798 // If complete is true and browser supports natural sizes, 799 // try to check for image status manually. 800 if ( this.img.complete && this.img.naturalWidth !== undefined ) { 801 // report based on naturalWidth 802 this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); 803 return; 804 } 805 806 // If none of the checks above matched, simulate loading on detached element. 807 var _this = this; 808 resource.on( 'confirm', function( resrc, message ) { 809 _this.confirm( resrc.isLoaded, message ); 810 return true; 811 } ); 812 813 resource.check(); 814 }; 815 816 LoadingImage.prototype.confirm = function( isLoaded, message ) { 817 this.isLoaded = isLoaded; 818 this.emit( 'confirm', this, message ); 819 }; 820 821 // -------------------------- Resource -------------------------- // 822 823 // Resource checks each src, only once 824 // separate class from LoadingImage to prevent memory leaks. See #115 825 826 var cache = {}; 827 828 function Resource( src ) { 829 this.src = src; 830 // add to cache 831 cache[ src ] = this; 832 } 833 834 Resource.prototype = new EventEmitter(); 835 836 Resource.prototype.check = function() { 837 // only trigger checking once 838 if ( this.isChecked ) { 839 return; 840 } 841 // simulate loading on detached element 842 var proxyImage = new Image(); 843 eventie.bind( proxyImage, 'load', this ); 844 eventie.bind( proxyImage, 'error', this ); 845 proxyImage.src = this.src; 846 // set flag 847 this.isChecked = true; 848 }; 849 850 // ----- events ----- // 851 852 // trigger specified handler for event type 853 Resource.prototype.handleEvent = function( event ) { 854 var method = 'on' + event.type; 855 if ( this[ method ] ) { 856 this[ method ]( event ); 857 } 858 }; 859 860 Resource.prototype.onload = function( event ) { 861 this.confirm( true, 'onload' ); 862 this.unbindProxyEvents( event ); 863 }; 864 865 Resource.prototype.onerror = function( event ) { 866 this.confirm( false, 'onerror' ); 867 this.unbindProxyEvents( event ); 868 }; 869 870 // ----- confirm ----- // 871 872 Resource.prototype.confirm = function( isLoaded, message ) { 873 this.isConfirmed = true; 874 this.isLoaded = isLoaded;
875 this.emit( 'confirm', this, message ); 876 }; 877 878 Resource.prototype.unbindProxyEvents = function( event ) { 879 eventie.unbind( event.target, 'load', this ); 880 eventie.unbind( event.target, 'error', this ); 881 }; 882 883 // ----- ----- // 884 885 return ImagesLoaded; 886 887} );
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.