1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
|
/*
* waitForImages 1.4
* -----------------
* Provides a callback when all images have loaded in your given selector.
* http://www.alexanderdickson.com/
*
*
* Copyright (c) 2011 Alex Dickson
* Licensed under the MIT licenses.
* See website for more info.
*
*/
;(function($) {
// Namespace all events.
var eventNamespace = 'waitForImages';
// CSS properties which contain references to images.
$.waitForImages = {
hasImageProperties: [
'backgroundImage',
'listStyleImage',
'borderImage',
'borderCornerImage'
]
};
// Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded.
$.expr[':'].uncached = function(obj) {
// Ensure we are dealing with an `img` element with a valid `src` attribute.
if ( ! $(obj).is('img[src!=""]')) {
return false;
}
// Firefox's `complete` property will always be`true` even if the image has not been downloaded.
// Doing it this way works in Firefox.
var img = document.createElement('img');
img.src = obj.src;
return ! img.complete;
};
$.fn.waitForImages = function(finishedCallback, eachCallback, waitForAll) {
// Handle options object.
if ($.isPlainObject(arguments[0])) {
eachCallback = finishedCallback.each;
waitForAll = finishedCallback.waitForAll;
finishedCallback = finishedCallback.finished;
}
// Handle missing callbacks.
finishedCallback = finishedCallback || $.noop;
eachCallback = eachCallback || $.noop;
// Convert waitForAll to Boolean
waitForAll = !! waitForAll;
// Ensure callbacks are functions.
if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
throw new TypeError('An invalid callback was supplied.');
};
return this.each(function() {
// Build a list of all imgs, dependent on what images will be considered.
var obj = $(this),
allImgs = [];
if (waitForAll) {
// CSS properties which may contain an image.
var hasImgProperties = $.waitForImages.hasImageProperties || [],
matchUrl = /url\((['"]?)(.*?)\1\)/g;
// Get all elements, as any one of them could have a background image.
obj.find('*').each(function() {
var element = $(this);
// If an `img` element, add it. But keep iterating in case it has a background image too.
if (element.is('img:uncached')) {
allImgs.push({
src: element.attr('src'),
element: element[0]
});
}
$.each(hasImgProperties, function(i, property) {
var propertyValue = element.css(property);
// If it doesn't contain this property, skip.
if ( ! propertyValue) {
return true;
}
// Get all url() of this element.
var match;
while (match = matchUrl.exec(propertyValue)) {
allImgs.push({
src: match[2],
element: element[0]
});
};
});
});
} else {
// For images only, the task is simpler.
obj
.find('img:uncached')
.each(function() {
allImgs.push({
src: this.src,
element: this
});
});
};
var allImgsLength = allImgs.length,
allImgsLoaded = 0;
// If no images found, don't bother.
if (allImgsLength == 0) {
finishedCallback.call(obj[0]);
};
$.each(allImgs, function(i, img) {
var image = new Image;
// Handle the image loading and error with the same callback.
$(image).bind('load.' + eventNamespace + ' error.' + eventNamespace, function(event) {
allImgsLoaded++;
// If an error occurred with loading the image, set the third argument accordingly.
eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load');
if (allImgsLoaded == allImgsLength) {
finishedCallback.call(obj[0]);
return false;
};
});
image.src = img.src;
});
});
};
})(jQuery);
|