reprogl

Форк
0
/
imagesloaded.js 
893 строки · 28.1 Кб
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 exports = this;
29
    var originalGlobalValue = exports.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 containing 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
        exports.EventEmitter = originalGlobalValue;
464
        return EventEmitter;
465
    };
466

467
    // Expose the class either via AMD, CommonJS or the global object
468
    if (typeof define === 'function' && define.amd) {
469
        define('eventEmitter/EventEmitter',[],function () {
470
            return EventEmitter;
471
        });
472
    }
473
    else if (typeof module === 'object' && module.exports){
474
        module.exports = EventEmitter;
475
    }
476
    else {
477
        this.EventEmitter = EventEmitter;
478
    }
479
}.call(this));
480

481
/*!
482
 * eventie v1.0.4
483
 * event binding helper
484
 *   eventie.bind( elem, 'click', myFn )
485
 *   eventie.unbind( elem, 'click', myFn )
486
 */
487

488
/*jshint browser: true, undef: true, unused: true */
489
/*global define: false */
490

491
( function( window ) {
492

493

494

495
var docElem = document.documentElement;
496

497
var bind = function() {};
498

499
function getIEEvent( obj ) {
500
  var event = window.event;
501
  // add event.target
502
  event.target = event.target || event.srcElement || obj;
503
  return event;
504
}
505

506
if ( docElem.addEventListener ) {
507
  bind = function( obj, type, fn ) {
508
    obj.addEventListener( type, fn, false );
509
  };
510
} else if ( docElem.attachEvent ) {
511
  bind = function( obj, type, fn ) {
512
    obj[ type + fn ] = fn.handleEvent ?
513
      function() {
514
        var event = getIEEvent( obj );
515
        fn.handleEvent.call( fn, event );
516
      } :
517
      function() {
518
        var event = getIEEvent( obj );
519
        fn.call( obj, event );
520
      };
521
    obj.attachEvent( "on" + type, obj[ type + fn ] );
522
  };
523
}
524

525
var unbind = function() {};
526

527
if ( docElem.removeEventListener ) {
528
  unbind = function( obj, type, fn ) {
529
    obj.removeEventListener( type, fn, false );
530
  };
531
} else if ( docElem.detachEvent ) {
532
  unbind = function( obj, type, fn ) {
533
    obj.detachEvent( "on" + type, obj[ type + fn ] );
534
    try {
535
      delete obj[ type + fn ];
536
    } catch ( err ) {
537
      // can't delete window object properties
538
      obj[ type + fn ] = undefined;
539
    }
540
  };
541
}
542

543
var eventie = {
544
  bind: bind,
545
  unbind: unbind
546
};
547

548
// transport
549
if ( typeof define === 'function' && define.amd ) {
550
  // AMD
551
  define( 'eventie/eventie',eventie );
552
} else {
553
  // browser global
554
  window.eventie = eventie;
555
}
556

557
})( this );
558

559
/*!
560
 * imagesLoaded v3.1.8
561
 * JavaScript is all like "You images are done yet or what?"
562
 * MIT License
563
 */
564

565
( function( window, factory ) {
566
  // universal module definition
567

568
  /*global define: false, module: false, require: false */
569

570
  if ( typeof define === 'function' && define.amd ) {
571
    // AMD
572
    define( [
573
      'eventEmitter/EventEmitter',
574
      'eventie/eventie'
575
    ], function( EventEmitter, eventie ) {
576
      return factory( window, EventEmitter, eventie );
577
    });
578
  } else if ( typeof exports === 'object' ) {
579
    // CommonJS
580
    module.exports = factory(
581
      window,
582
      require('wolfy87-eventemitter'),
583
      require('eventie')
584
    );
585
  } else {
586
    // browser global
587
    window.imagesLoaded = factory(
588
      window,
589
      window.EventEmitter,
590
      window.eventie
591
    );
592
  }
593

594
})( window,
595

596
// --------------------------  factory -------------------------- //
597

598
function factory( window, EventEmitter, eventie ) {
599

600

601

602
var $ = window.jQuery;
603
var console = window.console;
604
var hasConsole = typeof console !== 'undefined';
605

606
// -------------------------- helpers -------------------------- //
607

608
// extend objects
609
function extend( a, b ) {
610
  for ( var prop in b ) {
611
    a[ prop ] = b[ prop ];
612
  }
613
  return a;
614
}
615

616
var objToString = Object.prototype.toString;
617
function isArray( obj ) {
618
  return objToString.call( obj ) === '[object Array]';
619
}
620

621
// turn element or nodeList into an array
622
function makeArray( obj ) {
623
  var ary = [];
624
  if ( isArray( obj ) ) {
625
    // use object if already an array
626
    ary = obj;
627
  } else if ( typeof obj.length === 'number' ) {
628
    // convert nodeList to array
629
    for ( var i=0, len = obj.length; i < len; i++ ) {
630
      ary.push( obj[i] );
631
    }
632
  } else {
633
    // array of single index
634
    ary.push( obj );
635
  }
636
  return ary;
637
}
638

639
  // -------------------------- imagesLoaded -------------------------- //
640

641
  /**
642
   * @param {Array, Element, NodeList, String} elem
643
   * @param {Object or Function} options - if function, use as callback
644
   * @param {Function} onAlways - callback function
645
   */
646
  function ImagesLoaded( elem, options, onAlways ) {
647
    // coerce ImagesLoaded() without new, to be new ImagesLoaded()
648
    if ( !( this instanceof ImagesLoaded ) ) {
649
      return new ImagesLoaded( elem, options );
650
    }
651
    // use elem as selector string
652
    if ( typeof elem === 'string' ) {
653
      elem = document.querySelectorAll( elem );
654
    }
655

656
    this.elements = makeArray( elem );
657
    this.options = extend( {}, this.options );
658

659
    if ( typeof options === 'function' ) {
660
      onAlways = options;
661
    } else {
662
      extend( this.options, options );
663
    }
664

665
    if ( onAlways ) {
666
      this.on( 'always', onAlways );
667
    }
668

669
    this.getImages();
670

671
    if ( $ ) {
672
      // add jQuery Deferred object
673
      this.jqDeferred = new $.Deferred();
674
    }
675

676
    // HACK check async to allow time to bind listeners
677
    var _this = this;
678
    setTimeout( function() {
679
      _this.check();
680
    });
681
  }
682

683
  ImagesLoaded.prototype = new EventEmitter();
684

685
  ImagesLoaded.prototype.options = {};
686

687
  ImagesLoaded.prototype.getImages = function() {
688
    this.images = [];
689

690
    // filter & find items if we have an item selector
691
    for ( var i=0, len = this.elements.length; i < len; i++ ) {
692
      var elem = this.elements[i];
693
      // filter siblings
694
      if ( elem.nodeName === 'IMG' ) {
695
        this.addImage( elem );
696
      }
697
      // find children
698
      // no non-element nodes, #143
699
      var nodeType = elem.nodeType;
700
      if ( !nodeType || !( nodeType === 1 || nodeType === 9 || nodeType === 11 ) ) {
701
        continue;
702
      }
703
      var childElems = elem.querySelectorAll('img');
704
      // concat childElems to filterFound array
705
      for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
706
        var img = childElems[j];
707
        this.addImage( img );
708
      }
709
    }
710
  };
711

712
  /**
713
   * @param {Image} img
714
   */
715
  ImagesLoaded.prototype.addImage = function( img ) {
716
    var loadingImage = new LoadingImage( img );
717
    this.images.push( loadingImage );
718
  };
719

720
  ImagesLoaded.prototype.check = function() {
721
    var _this = this;
722
    var checkedCount = 0;
723
    var length = this.images.length;
724
    this.hasAnyBroken = false;
725
    // complete if no images
726
    if ( !length ) {
727
      this.complete();
728
      return;
729
    }
730

731
    function onConfirm( image, message ) {
732
      if ( _this.options.debug && hasConsole ) {
733
        console.log( 'confirm', image, message );
734
      }
735

736
      _this.progress( image );
737
      checkedCount++;
738
      if ( checkedCount === length ) {
739
        _this.complete();
740
      }
741
      return true; // bind once
742
    }
743

744
    for ( var i=0; i < length; i++ ) {
745
      var loadingImage = this.images[i];
746
      loadingImage.on( 'confirm', onConfirm );
747
      loadingImage.check();
748
    }
749
  };
750

751
  ImagesLoaded.prototype.progress = function( image ) {
752
    this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
753
    // HACK - Chrome triggers event before object properties have changed. #83
754
    var _this = this;
755
    setTimeout( function() {
756
      _this.emit( 'progress', _this, image );
757
      if ( _this.jqDeferred && _this.jqDeferred.notify ) {
758
        _this.jqDeferred.notify( _this, image );
759
      }
760
    });
761
  };
762

763
  ImagesLoaded.prototype.complete = function() {
764
    var eventName = this.hasAnyBroken ? 'fail' : 'done';
765
    this.isComplete = true;
766
    var _this = this;
767
    // HACK - another setTimeout so that confirm happens after progress
768
    setTimeout( function() {
769
      _this.emit( eventName, _this );
770
      _this.emit( 'always', _this );
771
      if ( _this.jqDeferred ) {
772
        var jqMethod = _this.hasAnyBroken ? 'reject' : 'resolve';
773
        _this.jqDeferred[ jqMethod ]( _this );
774
      }
775
    });
776
  };
777

778
  // -------------------------- jquery -------------------------- //
779

780
  if ( $ ) {
781
    $.fn.imagesLoaded = function( options, callback ) {
782
      var instance = new ImagesLoaded( this, options, callback );
783
      return instance.jqDeferred.promise( $(this) );
784
    };
785
  }
786

787

788
  // --------------------------  -------------------------- //
789

790
  function LoadingImage( img ) {
791
    this.img = img;
792
  }
793

794
  LoadingImage.prototype = new EventEmitter();
795

796
  LoadingImage.prototype.check = function() {
797
    // first check cached any previous images that have same src
798
    var resource = cache[ this.img.src ] || new Resource( this.img.src );
799
    if ( resource.isConfirmed ) {
800
      this.confirm( resource.isLoaded, 'cached was confirmed' );
801
      return;
802
    }
803

804
    // If complete is true and browser supports natural sizes,
805
    // try to check for image status manually.
806
    if ( this.img.complete && this.img.naturalWidth !== undefined ) {
807
      // report based on naturalWidth
808
      this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
809
      return;
810
    }
811

812
    // If none of the checks above matched, simulate loading on detached element.
813
    var _this = this;
814
    resource.on( 'confirm', function( resrc, message ) {
815
      _this.confirm( resrc.isLoaded, message );
816
      return true;
817
    });
818

819
    resource.check();
820
  };
821

822
  LoadingImage.prototype.confirm = function( isLoaded, message ) {
823
    this.isLoaded = isLoaded;
824
    this.emit( 'confirm', this, message );
825
  };
826

827
  // -------------------------- Resource -------------------------- //
828

829
  // Resource checks each src, only once
830
  // separate class from LoadingImage to prevent memory leaks. See #115
831

832
  var cache = {};
833

834
  function Resource( src ) {
835
    this.src = src;
836
    // add to cache
837
    cache[ src ] = this;
838
  }
839

840
  Resource.prototype = new EventEmitter();
841

842
  Resource.prototype.check = function() {
843
    // only trigger checking once
844
    if ( this.isChecked ) {
845
      return;
846
    }
847
    // simulate loading on detached element
848
    var proxyImage = new Image();
849
    eventie.bind( proxyImage, 'load', this );
850
    eventie.bind( proxyImage, 'error', this );
851
    proxyImage.src = this.src;
852
    // set flag
853
    this.isChecked = true;
854
  };
855

856
  // ----- events ----- //
857

858
  // trigger specified handler for event type
859
  Resource.prototype.handleEvent = function( event ) {
860
    var method = 'on' + event.type;
861
    if ( this[ method ] ) {
862
      this[ method ]( event );
863
    }
864
  };
865

866
  Resource.prototype.onload = function( event ) {
867
    this.confirm( true, 'onload' );
868
    this.unbindProxyEvents( event );
869
  };
870

871
  Resource.prototype.onerror = function( event ) {
872
    this.confirm( false, 'onerror' );
873
    this.unbindProxyEvents( event );
874
  };
875

876
  // ----- confirm ----- //
877

878
  Resource.prototype.confirm = function( isLoaded, message ) {
879
    this.isConfirmed = true;
880
    this.isLoaded = isLoaded;
881
    this.emit( 'confirm', this, message );
882
  };
883

884
  Resource.prototype.unbindProxyEvents = function( event ) {
885
    eventie.unbind( event.target, 'load', this );
886
    eventie.unbind( event.target, 'error', this );
887
  };
888

889
  // -----  ----- //
890

891
  return ImagesLoaded;
892

893
});

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.