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