-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathTo.ImageCache.js
327 lines (275 loc) · 7.07 KB
/
To.ImageCache.js
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
var c = {
folder: 'ToCache',
expireTime: 43200, // half a day (in seconds)
debug: false, // does console.log debug
remoteBackup: true // do you want the file(s) to be backed up to a remote cloud, like iCloud on iOS? Doesn't work on Android
};
var fileList = Ti.App.Properties.getList('To.ImageCache.ImageList',[]);
/**
* Set the config
* @param {Object} Config Object as per spec
*/
var config = function(config){
if (!config){
return;
}
if (config.debug){
Ti.API.info('TIC - setting config');
}
_.each(c, function(value, key){
if (config.hasOwnProperty(key)){
c[key] = config[key];
Ti.API.info('TIC - setting ' + key + ' to ' + config[key]);
};
});
};
/**
* Check if file based on filename is already in system
*/
var hasFile = function(filename){
if (c.debug){
Ti.API.info('TIC - checking file in system - ' + filename);
}
return _.findWhere(fileList, {filename: filename});
};
/**
* has the directory been created yet?
*/
var checkDir = function(){
var dir = Titanium.Filesystem.getFile(Titanium.Filesystem.applicationDataDirectory,c.folder);
if (!dir.exists()){
dir.createDirectory();
}
return true;
};
/**
* how big is the cache? This function will return total cache in bytes
*/
var cacheSize = function(){
var bytes = 0;
if (c.debug)
Ti.API.info('TIC - calculating cache size');
_.each(fileList, function(file){
bytes += file.fileSize;
});
return bytes;
};
/**
* Clear the cache entirely
*/
var clearCache = function(){
if (c.debug)
Ti.API.info('TIC - Completely emtying cache');
_.each(fileList, function(file){
removeFile(file.filename);
});
};
/**
* Clear only cache files that are older than cache expiry time
*/
var flushExpired = function(){
if (c.debug)
Ti.API.info('TIC - flush expired files');
var removeFiles = [];
_.each(fileList, function(file){
if (Date.now() - (file.added + (file.expireTime * 1000)) > 0){
if (c.debug)
Ti.API.info('TIC - found expired file, removing');
removeFiles.push(file.filename);
}
});
_.each(removeFiles, removeFile);
};
/**
* Remove a file based on internal filename
* Note: filename is generated by To.ImageCache
* @param {String} Filename of the image
*/
var removeFile = function(filename){
if (c.debug)
Ti.API.info('TIC - removing file ' + filename);
var file = hasFile(filename);
if (!file){
return false;
}
var path = Ti.Filesystem.applicationDataDirectory + file.folder;
var f = Ti.Filesystem.getFile(path, file.filename);
if (!f.exists()){
fileList = _.without(fileList, file);
Ti.App.Properties.setList('To.ImageCache.ImageList', fileList);
if (c.debug)
Ti.API.info('TIC - file has aleady been removed');
return false;
}
if (f.deleteFile()){
if (c.debug)
Ti.API.info('TIC - file has been removed');
} else {
fileList = _.without(fileList, file);
Ti.App.Properties.setList('To.ImageCache.ImageList', fileList);
}
};
function md5FileName(url){
var filename = Ti.Utils.md5HexDigest(url);
return filename;
}
/**
* Remove a file based on URL from cache.
* Useful if you don't know the filename
* @param {String} URL of the image
*/
var removeRemote = function(url){
if (c.debug)
Ti.API.info('TIC - removing file based on URL');
var filename = md5FileName(url);
removeFile(filename);
};
/**
* Store the file
* @param {String} filename (needs to be unique, otherwise will overwrite)
* @param {Blob} Blob of the image
*/
var storeFile = function(filename, blob, overwrite){
if (c.debug){
Ti.API.info('TIC - store file ' + filename);
}
// check if directory has been created
checkDir();
// we already have this file
if (!overwrite && hasFile(filename)){
blob = null;
return;
}
var path = Ti.Filesystem.applicationDataDirectory + c.folder;
var file = Ti.Filesystem.getFile(path, filename);
if (file.write(blob)){
// do we need to cache the file?
if (Ti.Platform.name == 'iPhone OS' && c.hasOwnProperty('remoteBackup')){
file.remoteBackup = c.remoteBackup;
}
}
var nPath = file.nativePath;
// destroy file after it has been saved
file = null;
// looks like there is no blob
if (!blob || !blob.length){
return;
}
fileList.push({
filename: filename,
added: Date.now(),
fileSize: blob.length,
expireTime: c.expireTime,
folder: c.folder
});
// add file to collection
Ti.App.Properties.setList('To.ImageCache.ImageList', fileList);
// destroy blob
blob = null;
return readFile(filename);
};
/**
* read file from memory
*/
var readFile = function(filename){
if (c.debug){
Ti.API.info('TIC - reading file from system ' + filename);
}
var file = hasFile(filename);
if (!file){
return false;
}
var path = Ti.Filesystem.applicationDataDirectory + file.folder;
var file = Ti.Filesystem.getFile(path, filename).read();
return file;
};
/**
* this function will always return a blob, wether it was cached or not
* in case it wasn't cached, it will do so after first fetching it.
* in case it was cached, it will just read the file and return the blob
* Therefore, only use this function if you want to cache it.
* @param {String} url
*/
var remoteImage = function(url){
if (c.debug){
Ti.API.info('TIC - *************');
Ti.API.info('remote image');
}
// calculate local filename
var filename = md5FileName(url);
Ti.API.info(filename);
if (hasFile(filename)){
if (c.debug){
Ti.API.info('TIC - has file in system');
Ti.API.info('TIC - *************');
}
// get file
return readFile(filename);
}
Ti.API.info('TIC - doesn\'t have file yet');
// generate a blob
var image = Ti.UI.createImageView({
image : url,
width : Ti.UI.SIZE,
height : Ti.UI.SIZE
});
var blob = image.toBlob();
image = null;
storeFile(filename, blob);
Ti.API.info('TIC - *************');
return blob;
};
/**
* This function will fetch the image in the background
* with a configurable cache period
* @param {String} url of the image to cache
* @param {Integer} (Optional) Timeout in milliseconds
* @param {Function} (Optional) callback function, blob will be returned
*/
var cache = function(url, timeout, cb){
var timeout = timeout || 30000;
// if file is already cached, don't do so again
var filename = md5FileName(url);
if (hasFile(filename)){
if (c.debug)
Ti.API.info('TIC - file already cached');
cb && cb(readFile(filename));
return false;
}
var xhr = Ti.Network.createHTTPClient({
onload: function() {
storeFile(filename, this.responseData);
cb && cb(readFile(filename));
},
timeout: timeout
});
xhr.open('GET', url);
xhr.send();
return true;
};
var storeBlob = function(name, blob, cb){
var filename = md5FileName('blob-' + name);
var nPath = storeFile(filename, blob, true);
cb && cb(nPath);
blob = null;
return readFile(filename);
};
var getBlob = function(name){
var filename = md5FileName('blob-' + name);
return readFile(filename);
};
/**
* only export what is needed externally
*/
module.exports = {
config: config,
cacheSize: cacheSize,
flushExpired: flushExpired,
clearCache: clearCache,
removeFile: removeFile,
removeRemote: removeRemote,
remoteImage: remoteImage,
cache: cache,
storeBlob: storeBlob,
getBlob: getBlob
};