Reputation: 15006
I have been playing around with Chromes filestorage API. I have built a couple of functions that together automatically downloads a json-object and stores it as a string. If the last server-request was done within 24 hours. I automatically use the last version of the file. I use this for managing a huge data-dump that I do statistical analysis on.
The entire system only has one function that needs to be exposed. It's getData
.
Currently all these functions are global variables. How should I make this contained in an orderly way.
//This file will cache serverdata every day.
var onInitFs,
errorHandler,
fileSystemInit,
saveFile,
readFile,
fileSystem,
getData;
//request rights to save files to system.
fileSystemInit = function(){
//Browser specific
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
navigator.webkitPersistentStorage.requestQuota(1048*1048*256, function(grantedBytes) {
//once approved (or if previously approved):
window.requestFileSystem(PERSISTENT, grantedBytes, onInitFs, errorHandler);
}, function(e) {
console.log('Error', e);
});
};
//make filesystem global.
onInitFs = function(fs) {
fileSystem = fs;
};
fileSystemInit();
saveFile = function(url, content, callback){
var filename = makeFilename(url)
if(!fileSystem){
console.log('no filesystem registered')
return;
}
fileSystem.root.getFile(filename, {create: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
var blob = new Blob([JSON.stringify(content)], {type: 'application/json'});
fileWriter.write(blob);
fileWriter.onwriteend = function(e) {
console.debug('Write completed.', e);
if(callback){
callback();
}
};
fileWriter.onerror = function(e) {
console.log('Write failed: ', e);
};
}, errorHandler);
}, errorHandler);
};
readFile = function(url, callback){
var filename = makeFilename(url)
if(!fileSystem){
console.log('no filesystem registered');
return;
}
fileSystem.root.getFile(filename, {}, function(fileEntry){
//this object reads files.
var reader = new FileReader();
//register callback for read files
reader.onloadend = function(e){
var callbackValue = JSON.parse(this.result)
callback(callbackValue);
};
//read file-function
fileEntry.file(function(file){
reader.readAsText(file);
},errorHandler);
},errorHandler);
};
makeFilename = function(url){
return url.replace(/\W/g, '') +'.json'
}
errorHandler = function(e) {
console.log('Error: ', e);
};
getData = function(url, callbackNewData, callbackOldData){
var lastDownloaded = localStorage.getItem(url+'lastDownloaded'),
oneDay = 1000*60*60*24;
//update data if the data is old.
window.setTimeout(function(){
if(!lastDownloaded || new Date()-new Date(lastDownloaded) > oneDay ){
console.debug('downloading '+url);
d3.json(url, function(data){
localStorage.setItem(url+'lastDownloaded',new Date());
console.debug('saving '+url);
saveFile(url, data, function(){
callbackNewData(url);
});
});
}else{
callbackOldData(url);
}
}, 200);
};
Upvotes: 2
Views: 127
Reputation: 28722
Just prototype the heck out of it :-) And use some scoped instances(using var that = this
) to pass elements back to the parent objects from different scopes.
Now you can just start a new FileSystemInstance()
to do your magic.
If you wish to make the more "arcane" methods private you could consider moving them to a object within your object and such, but in the end anyone with true perserverance will be able to access them. So I advice to go with a public way, and name the private methods _fileSystemInit
, so people who read the code know it's an internalised method.
//This file will cache serverdata every day.
function FileSystemInstance() {
this.fileSystem = null;
this.requestFileSystem = null;
this.fileSystemInit();
}
//request rights to save files to system.
FileSystemInstance.prototype.fileSystemInit = function(){
//Browser specific
this.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
this.requestFileSystem = this.requestFileSystem.bind(window);
console.log(this.requestFileSystem);
var that = this;
console.log(that.requestFileSystem);
navigator.webkitPersistentStorage.requestQuota(1048*1048*256, function(grantedBytes) {
//once approved (or if previously approved):
console.log(that.requestFileSystem);
that.requestFileSystem(PERSISTENT, grantedBytes, function(fs){that.onInitFs(fs)}, function(e){that.errorHandler(e)});
}, function(e) {
console.log('Error', e);
});
};
//make filesystem global.
FileSystemInstance.prototype.onInitFs = function(fs) {
this.fileSystem = fs;
};
FileSystemInstance.prototype.saveFile = function(url, content, callback){
var filename = this.makeFilename(url)
if(!fileSystem){
console.log('no filesystem registered')
return;
}
this.fileSystem.root.getFile(filename, {create: true}, function(fileEntry) {
fileEntry.createWriter(function(fileWriter) {
var blob = new Blob([JSON.stringify(content)], {type: 'application/json'});
fileWriter.write(blob);
fileWriter.onwriteend = function(e) {
console.debug('Write completed.', e);
if(callback){
callback();
}
};
fileWriter.onerror = function(e) {
console.log('Write failed: ', e);
};
}, errorHandler);
}, errorHandler);
};
FileSystemInstance.prototype.readFile = function(url, callback){
var filename = this.makeFilename(url)
if(!this.fileSystem){
throw new Error('no filesystem registered');
}
this.fileSystem.root.getFile(filename, {}, function(fileEntry){
//this object reads files.
var reader = new FileReader();
//register callback for read files
reader.onloadend = function(e){
var callbackValue = JSON.parse(this.result)
callback(callbackValue);
};
//read file-function
fileEntry.file(function(file){
reader.readAsText(file);
},errorHandler);
},errorHandler);
};
FileSystemInstance.prototype.makeFilename = function(url){
return url.replace(/\W/g, '') +'.json'
}
FileSystemInstance.prototype.errorHandler = function(e) {
console.error('Error: ', e);
};
FileSystemInstance.prototype.getData = function(url, callbackNewData, callbackOldData){
var that = this;
var lastDownloaded = localStorage.getItem(url+'lastDownloaded'),
oneDay = 1000*60*60*24;
//update data if the data is old.
window.setTimeout(function(){
if(!lastDownloaded || new Date()-new Date(lastDownloaded) > oneDay ){
console.debug('downloading '+url);
d3.json(url, function(data){
localStorage.setItem(url+'lastDownloaded',new Date());
console.debug('saving '+url);
that.saveFile(url, data, function(){
callbackNewData(url);
});
});
}else{
callbackOldData(url);
}
}, 200);
};
FileSystem = new FileSystemInstance();
var data = FileSystem.getData();
console.log("Data is: ",data);
Upvotes: 0
Reputation: 1058
The Module
pattern would be a good start: http://addyosmani.com/resources/essentialjsdesignpatterns/book/#modulepatternjavascript
Pseudo-class also would be great:
var StorageInterface = function(arg1, arg2){
this.arg1 = arg1;
this.arg2 = arg2;
}
StorageInterface.prototype.method = function(arg3, arg4){
return arg3 + arg4 + this.arg1 + this.arg2;
}
var si = new StorageInterface(100, 500);
si.method(3, 4);
Upvotes: 0
Reputation: 7100
(function(window){
'use strict'
var onInitFs,errorHandler,fileSystemInit,saveFile,readFile,fileSystem,getData;
fileSystemInit = function(){
// your code
};
//make filesystem global.
onInitFs = function(fs) {
//your code
};
fileSystemInit();
saveFile = function(url, content, callback){
//your code
};
readFile = function(url, callback){
//your code
};
makeFilename = function(url){
//your code
}
errorHandler = function(e) {
//your code
};
getData = function(url, callbackNewData, callbackOldData){
//your code
};
window.HimmatorsFileStorageAPI = getData; // you can change the name here
})(window);
And you can use it simply by including this script in your page and then calling
HimmatorsFileStorageAPI(url, callbackNewData, callbackOldData);
Upvotes: 0
Reputation: 5334
You can wrap the whole thing in an anonymous function and expose getData only. This is the easiest way to do.
var getDataFromUrl = function () {
//This file will cache serverdata every day.
var onInitFs,
errorHandler,
fileSystemInit,
saveFile,
readFile,
fileSystem,
getData;
// Your original code here ...
return getData; // This exposes the getData function.
})();
In this way you only exposes one global function getDataFromUrl
, which is exactly the public API.
For more modern usage, you may want to check out Common JS Modules and Browserify, which let you do exports
and require
both in browser and NodeJS. There is also a UMD Pattern for exporting libraries.
Upvotes: 2