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
|
/*
timb:
the ImgCache manages loading images and keeping track of image sizes...
it can be passed a bunch of urls to load and a callback that gets called when more images are ready
image loading can also be paused and started again.
there can be separate ImageCaches, eg, one for search result images, one for chat images (but they all share the actual image cache)
In theory it should also avoid a few http requests bc we can just dup DOM nodes for images that are already loaded that don't have cache headers (not sure tho, browsers probably pretty aggressive with that already)
*/
var ImgCache = {
"imgs": {}, // <img> nodes indexed by url
"caches": {},
"init": function(name){
// don't clear callback
var callback = emptyFunc
if (name in ImgCache.caches)
callback = ImgCache.caches[name].onImgsLoaded
delete ImgCache.caches[name]
ImgCache.caches[name] = {
"loadAtATime": 10,
"urlsToLoad": [],
"imgsLoading": {},
"imgsLoadingCounter" : 0, // a hack so i don't have to iterate over the object to always get its size...
"imgsLoaded": {},
"onImgsLoaded": callback,
"paused": false
}
},
"add": function(name, urls){
if (!(name in ImgCache.caches)) ImgCache.init(name)
if (!$.isArray(urls)) urls = [urls];
var cache = ImgCache.caches[name]
urls.forEach(function(url){
cache.urlsToLoad.push(url)
})
},
"config": function(name, cfg){
if (!(name in ImgCache.caches)) ImgCache.init(name)
var cache = ImgCache.caches[name]
for(var key in cfg)
cache[key] = cfg[key]
},
"clear": function(name){ ImgCache.init(name) },
"pause": function(name){ ImgCache.caches[name].paused = true },
"unpause": function(name){ ImgCache.caches[name].paused = false },
"loadImages": function(cache){
if (cache.paused) return;
while(cache.urlsToLoad.length && cache.imgsLoadingCounter < cache.loadAtATime) {
var url = cache.urlsToLoad.shift()
if (url in ImgCache.imgs) { // already loading this image
var img = ImgCache.imgs[url]
if (img.complete) {
cache.imgsLoaded[url] = ImgCache.imgs[url]
} else if (!(url in cache.imgsLoading)) {
cache.imgsLoading[url] = ImgCache.imgs[url]
cache.imgsLoadingCounter += 1
}
} else {
var img = new Image()
img.src = url
img.animated = (parseUri(url)["file"].toLowerCase().substr(-3) == "gif") ? true : false;
ImgCache.imgs[url] = img
cache.imgsLoading[url] = img
cache.imgsLoadingCounter += 1
}
}
},
"processLoadingImages": function(cache){
for (var url in cache.imgsLoading) {
var img = cache.imgsLoading[url]
if (img.complete) {
cache.imgsLoaded[url] = img
delete cache.imgsLoading[url]
cache.imgsLoadingCounter -= 1
}
}
},
"loader": function(){
for (name in ImgCache.caches){
var cache = ImgCache.caches[name]
ImgCache.processLoadingImages(cache) // move images from imgsLoading into imgsLoaded
ImgCache.loadImages(cache) // put new images in imgsLoading/imgsLoaded from urlsToLoad
for (var url in cache.imgsLoaded) {
cache.onImgsLoaded(cache.imgsLoaded) // only call if new images actually loaded
delete cache.imgsLoaded
cache.imgsLoaded = {}
break;
}
}
setTimeout(ImgCache.loader, 500)
}
}
ImgCache.loader()
// parseUri 1.2.2 from http://blog.stevenlevithan.com/archives/parseuri
// (c) Steven Levithan <stevenlevithan.com>, MIT License
// timb: todo: this can't deal with @s in urls correctly. ex: http://www.classicbattletech.com/images/gallery/Combat_Operations_Cover@1280x960.jpg
function parseUri (str) {
var o = parseUri.options,
m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
uri = {},
i = 14;
while (i--) uri[o.key[i]] = m[i] || "";
uri[o.q.name] = {};
uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
if ($1) uri[o.q.name][$1] = $2;
});
return uri;
};
parseUri.options = {
strictMode: false,
key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
q: {
name: "queryKey",
parser: /(?:^|&)([^&=]*)=?([^&]*)/g
},
parser: {
strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
}
};
// end parseUri
|