mirror of
https://github.com/kiwix/kiwix-js.git
synced 2025-09-22 03:52:21 -04:00
Storage abstraction.
This commit is contained in:
parent
44e4903598
commit
a4ea0c8048
@ -34,6 +34,7 @@ define(function(require) {
|
||||
var util = require('util');
|
||||
var cookies = require('cookies');
|
||||
var geometry = require('geometry');
|
||||
var osabstraction = require('osabstraction');
|
||||
|
||||
// Maximum number of titles to display in a search
|
||||
var MAX_SEARCH_RESULT_SIZE = 50;
|
||||
@ -155,12 +156,15 @@ define(function(require) {
|
||||
// We have to scan all the DeviceStorages, because getDeviceStorage
|
||||
// only returns the default Device Storage.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=885753
|
||||
storages = navigator.getDeviceStorages("sdcard");
|
||||
storages = $.map(navigator.getDeviceStorages("sdcard"), function(s) {
|
||||
return new osabstraction.StorageFirefoxOS(s);
|
||||
});
|
||||
}
|
||||
else {
|
||||
// The method getDeviceStorages is not available (FxOS 1.0)
|
||||
// The fallback is to use getDeviceStorage
|
||||
storages[0] = navigator.getDeviceStorage("sdcard");
|
||||
storages[0] = new osabstraction.StorageFirefoxOS(
|
||||
navigator.getDeviceStorage("sdcard"));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,7 @@ define(function(require) {
|
||||
var evopediaTitle = require('title');
|
||||
var util = require('util');
|
||||
var geometry = require('geometry');
|
||||
var jQuery = require('jquery');
|
||||
|
||||
// Declare the webworker that can uncompress with bzip2 algorithm
|
||||
var webworkerBzip2 = new Worker("js/lib/webworker_bzip2.js");
|
||||
@ -69,20 +70,16 @@ define(function(require) {
|
||||
*/
|
||||
LocalArchive.prototype.readTitleFilesFromStorage = function(storage, directory) {
|
||||
var currentLocalArchiveInstance = this;
|
||||
var filerequest = storage.get(directory + 'titles.idx');
|
||||
filerequest.onsuccess = function() {
|
||||
currentLocalArchiveInstance.titleFile = filerequest.result;
|
||||
};
|
||||
filerequest.onerror = function(event) {
|
||||
alert("Error reading title file in directory " + directory + " : " + event.target.error.name);
|
||||
};
|
||||
var filerequestSearch = storage.get(directory + 'titles_search.idx');
|
||||
filerequestSearch.onsuccess = function() {
|
||||
currentLocalArchiveInstance.titleSearchFile = filerequest.result;
|
||||
};
|
||||
filerequest.onerror = function(event) {
|
||||
storage.get(directory + 'titles.idx').then(function(file) {
|
||||
currentLocalArchiveInstance.titleFile = file;
|
||||
}, function(error) {
|
||||
alert("Error reading title file in directory " + directory + " : " + error);
|
||||
});
|
||||
storage.get(directory + 'titles_search.idx').then(function(file) {
|
||||
currentLocalArchiveInstance.titleSearchFile = file;
|
||||
}, function(error) {
|
||||
// Do nothing : this file is not mandatory in an archive
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -102,20 +99,18 @@ define(function(require) {
|
||||
} else {
|
||||
prefixedFileNumber = index;
|
||||
}
|
||||
var filerequest = storage.get(directory + 'wikipedia_' + prefixedFileNumber
|
||||
+ '.dat');
|
||||
filerequest.onsuccess = function() {
|
||||
currentLocalArchiveInstance.dataFiles[index] = filerequest.result;
|
||||
currentLocalArchiveInstance.readDataFilesFromStorage(storage, directory,
|
||||
index + 1);
|
||||
};
|
||||
filerequest.onerror = function(event) {
|
||||
// TODO there must be a better to way to detect a FileNotFound
|
||||
if (event.target.error.name != "NotFoundError") {
|
||||
alert("Error reading data file " + index + " in directory "
|
||||
+ directory + " : " + event.target.error.name);
|
||||
}
|
||||
};
|
||||
storage.get(directory + 'wikipedia_' + prefixedFileNumber + '.dat')
|
||||
.then(function(file) {
|
||||
currentLocalArchiveInstance.dataFiles[index] = file;
|
||||
currentLocalArchiveInstance.readDataFilesFromStorage(storage, directory,
|
||||
index + 1);
|
||||
}, function(error) {
|
||||
// TODO there must be a better to way to detect a FileNotFound
|
||||
if (error != "NotFoundError") {
|
||||
alert("Error reading data file " + index + " in directory "
|
||||
+ directory + " : " + error);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -135,20 +130,18 @@ define(function(require) {
|
||||
} else {
|
||||
prefixedFileNumber = index;
|
||||
}
|
||||
var filerequest = storage.get(directory + 'coordinates_' + prefixedFileNumber
|
||||
+ '.idx');
|
||||
filerequest.onsuccess = function() {
|
||||
currentLocalArchiveInstance.coordinateFiles[index - 1] = filerequest.result;
|
||||
storage.get(directory + 'coordinates_' + prefixedFileNumber
|
||||
+ '.idx').then(function(file) {
|
||||
currentLocalArchiveInstance.coordinateFiles[index] = file;
|
||||
currentLocalArchiveInstance.readCoordinateFilesFromStorage(storage, directory,
|
||||
index + 1);
|
||||
};
|
||||
filerequest.onerror = function(event) {
|
||||
}, function(error) {
|
||||
// TODO there must be a better to way to detect a FileNotFound
|
||||
if (event.target.error.name != "NotFoundError") {
|
||||
if (error != "NotFoundError") {
|
||||
alert("Error reading coordinates file " + index + " in directory "
|
||||
+ directory + " : " + event.target.error.name);
|
||||
+ directory + " : " + error);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -161,15 +154,13 @@ define(function(require) {
|
||||
LocalArchive.prototype.readMetadataFileFromStorage = function(storage, directory) {
|
||||
var currentLocalArchiveInstance = this;
|
||||
|
||||
var filerequest = storage.get(directory + 'metadata.txt');
|
||||
filerequest.onsuccess = function() {
|
||||
var metadataFile = filerequest.result;
|
||||
storage.get(directory + 'metadata.txt').then(function(file) {
|
||||
var metadataFile = file;
|
||||
currentLocalArchiveInstance.readMetadataFile(metadataFile);
|
||||
};
|
||||
filerequest.onerror = function(event) {
|
||||
}, function(error) {
|
||||
alert("Error reading metadata.txt file in directory "
|
||||
+ directory + " : " + event.target.error.name);
|
||||
};
|
||||
+ directory + " : " + error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -300,20 +291,16 @@ define(function(require) {
|
||||
*/
|
||||
LocalArchive.prototype.readMathFilesFromStorage = function(storage, directory) {
|
||||
var currentLocalArchiveInstance = this;
|
||||
var filerequest1 = storage.get(directory + 'math.idx');
|
||||
filerequest1.onsuccess = function() {
|
||||
currentLocalArchiveInstance.mathIndexFile = filerequest1.result;
|
||||
};
|
||||
filerequest1.onerror = function(event) {
|
||||
alert("Error reading math index file in directory " + directory + " : " + event.target.error.name);
|
||||
};
|
||||
var filerequest2 = storage.get(directory + 'math.dat');
|
||||
filerequest2.onsuccess = function() {
|
||||
currentLocalArchiveInstance.mathDataFile = filerequest2.result;
|
||||
};
|
||||
filerequest2.onerror = function(event) {
|
||||
alert("Error reading math data file in directory " + directory + " : " + event.target.error.name);
|
||||
};
|
||||
storage.get(directory + 'math.idx').then(function(file) {
|
||||
currentLocalArchiveInstance.mathIndexFile = file;
|
||||
}, function(error) {
|
||||
alert("Error reading math index file in directory " + directory + " : " + error);
|
||||
});
|
||||
storage.get(directory + 'math.dat').then(function(file) {
|
||||
currentLocalArchiveInstance.mathDataFile = file;
|
||||
}, function(error) {
|
||||
alert("Error reading math data file in directory " + directory + " : " + error);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@ -928,40 +915,23 @@ define(function(require) {
|
||||
*/
|
||||
LocalArchive.scanForArchives = function(storages, callbackFunction) {
|
||||
var directories = [];
|
||||
var cursor = util.enumerateAll(storages);
|
||||
cursor.onerror = function() {
|
||||
alert("Error scanning your SD card : " + cursor.error
|
||||
+". If you're using the Firefox OS Simulator, please put the archives in a 'fake-sdcard' directory inside your Firefox profile (ex : ~/.mozilla/firefox/xxxx.default/extensions/r2d2b2g@mozilla.org/profile/fake-sdcard/wikipedia_small_2010-08-14)");
|
||||
var promises = jQuery.map(storages, function(storage) {
|
||||
return storage.scanForDirectoriesContainingFile('titles.idx')
|
||||
.then(function(dirs) {
|
||||
jQuery.merge(directories, dirs);
|
||||
return true
|
||||
});
|
||||
});
|
||||
jQuery.when.apply(null, promises).then(function() {
|
||||
callbackFunction(directories);
|
||||
}, function(error) {
|
||||
alert("Error scanning your SD card : " + error
|
||||
+ ". If you're using the Firefox OS Simulator, please put the archives in "
|
||||
+ "a 'fake-sdcard' directory inside your Firefox profile "
|
||||
+ "(ex : ~/.mozilla/firefox/xxxx.default/extensions/r2d2b2g@mozilla.org/"
|
||||
+ "profile/fake-sdcard/wikipedia_small_2010-08-14)");
|
||||
callbackFunction(null);
|
||||
};
|
||||
cursor.onsuccess = function() {
|
||||
if (cursor.result) {
|
||||
var file = cursor.result;
|
||||
var fileName = file.name;
|
||||
|
||||
// We look for files "titles.idx"
|
||||
if (!util.endsWith(fileName, "titles.idx")) {
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the case of archive files at the root of the sd-card
|
||||
// (without a subdirectory)
|
||||
var directory = "/";
|
||||
|
||||
if (fileName.lastIndexOf('/')!==-1) {
|
||||
// We want to return the directory where titles.idx is stored
|
||||
// We also keep the trailing slash
|
||||
directory = fileName.substring(0, fileName.lastIndexOf('/') + 1);
|
||||
}
|
||||
|
||||
directories.push(directory);
|
||||
cursor.continue();
|
||||
}
|
||||
else {
|
||||
callbackFunction(directories);
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
193
www/js/lib/osabstraction.js
Normal file
193
www/js/lib/osabstraction.js
Normal file
@ -0,0 +1,193 @@
|
||||
/**
|
||||
* osabstraction.js: Abstraction layer for file access
|
||||
*
|
||||
* Copyright 2014 Evopedia developers
|
||||
* License GPL v3:
|
||||
*
|
||||
* This file is part of Evopedia.
|
||||
*
|
||||
* Evopedia is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Evopedia is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Evopedia (file LICENSE-GPLv3.txt). If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
define(['util', 'jquery'], function(util, jQuery) {
|
||||
/**
|
||||
* Creates an abstraction layer around the FirefoxOS storage.
|
||||
* @see StoragePhoneGap
|
||||
* @param storage FirefoxOS DeviceStorage object
|
||||
*/
|
||||
function StorageFirefoxOS(storage) {
|
||||
this._storage = storage;
|
||||
this.storageName = storage.storageName;
|
||||
}
|
||||
/**
|
||||
* Access the given file.
|
||||
* @param path absolute path to the file
|
||||
* @return jQuery promise which is resolved with a HTML5 file object and
|
||||
* rejected with an error message.
|
||||
*/
|
||||
StorageFirefoxOS.prototype.get = function(path) {
|
||||
var deferred = jQuery.Deferred();
|
||||
var request = this._storage.get(path);
|
||||
request.onsuccess = function() { deferred.resolve(this.result); }
|
||||
request.onerror = function() { deferred.reject(this.error.name); }
|
||||
return deferred.promise();
|
||||
}
|
||||
/**
|
||||
* Searches for directories containing a file with the given name.
|
||||
* @param fileName file name to search
|
||||
* @return jQuery promise which is resolved with an array of directory
|
||||
* paths and rejected with an error message.
|
||||
*/
|
||||
StorageFirefoxOS.prototype.scanForDirectoriesContainingFile
|
||||
= function(fileName) {
|
||||
var deferred = jQuery.Deferred();
|
||||
var directories = [];
|
||||
var cursor = this._storage.enumerate();
|
||||
cursor.onerror = function() {
|
||||
deferred.reject(cursor.error);
|
||||
};
|
||||
cursor.onsuccess = function() {
|
||||
if (!cursor.result) {
|
||||
deferred.resolve(directories);
|
||||
return;
|
||||
}
|
||||
var file = cursor.result;
|
||||
|
||||
if (!util.endsWith(file.name, fileName)) {
|
||||
cursor.continue();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle the case of archive files at the root of the sd-card
|
||||
// (without a subdirectory)
|
||||
var directory = "/";
|
||||
|
||||
if (file.name.lastIndexOf('/') !== -1) {
|
||||
// We want to return the directory where the file is stored
|
||||
// We also keep the trailing slash
|
||||
directory = file.name.substring(0,
|
||||
file.name.lastIndexOf('/') + 1);
|
||||
}
|
||||
directories.push(directory);
|
||||
|
||||
cursor.continue();
|
||||
};
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an abstraction layour around the PhoneGap storage.
|
||||
* @see StorageFirefoxOS
|
||||
* @param storage PhoneGap FileSystem object
|
||||
*/
|
||||
function StoragePhoneGap(storage) {
|
||||
this._storage = storage;
|
||||
this.storageName = 'PhoneGapStorage'; // TODO
|
||||
}
|
||||
/**
|
||||
* Access the given file.
|
||||
* @param path absolute path to the file
|
||||
* @return jQuery promise which is resolved with a HTML5 file object and
|
||||
* rejected with an error message.
|
||||
*/
|
||||
StoragePhoneGap.prototype.get = function(path) {
|
||||
console.log("Trying to access " + path);
|
||||
var deferred = jQuery.Deferred();
|
||||
var that = this;
|
||||
var onSuccess = function(file) {
|
||||
deferred.resolve(file);
|
||||
};
|
||||
var onError = function(error) {
|
||||
console.log("Error code: " + error.code);
|
||||
deferred.reject(that._errorCodeToString(error.code));
|
||||
};
|
||||
var onSuccessInt = function(fileEntry) {
|
||||
fileEntry.file(onSuccess, onError);
|
||||
};
|
||||
var options = {create: false, exclusive: false};
|
||||
if (path.substr(0, 7) == 'file://')
|
||||
path = path.substr(7);
|
||||
this._storage.root.getFile(path, options, onSuccessInt, onError);
|
||||
return deferred.promise();
|
||||
}
|
||||
/**
|
||||
* Searches for directories containing a file with the given name.
|
||||
* @param fileName file name to search
|
||||
* @return jQuery promise which is resolved with an array of directory
|
||||
* paths and rejected with an error message.
|
||||
*/
|
||||
StoragePhoneGap.prototype.scanForDirectoriesContainingFile
|
||||
= function(fileName) {
|
||||
var that = this;
|
||||
var deferred = jQuery.Deferred();
|
||||
var directories = [];
|
||||
var stack = [this._storage.root];
|
||||
|
||||
var dirReaderSuccess = function(entries) {
|
||||
var dir = stack[stack.length - 1];
|
||||
stack.pop();
|
||||
for (var i = 0; i < entries.length; i ++) {
|
||||
var entry = entries[i];
|
||||
if (entry.isDirectory) {
|
||||
stack.push(entry);
|
||||
} else if (util.endsWith(entry.name, fileName)) {
|
||||
var path = dir.fullPath;
|
||||
if (path.length == 0 || path[path.length - 1] != '/')
|
||||
path += '/';
|
||||
directories.push(path);
|
||||
}
|
||||
}
|
||||
iteration();
|
||||
}
|
||||
var dirReaderFail = function(error) {
|
||||
deferred.reject(that._errorCodeToString(error.code));
|
||||
}
|
||||
var iteration = function() {
|
||||
if (stack.length == 0) {
|
||||
deferred.resolve(directories);
|
||||
return;
|
||||
}
|
||||
var reader = stack[stack.length - 1].createReader();
|
||||
reader.readEntries(dirReaderSuccess, dirReaderFail);
|
||||
}
|
||||
iteration();
|
||||
return deferred.promise();
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert HTML5 FileError codes to strings.
|
||||
* @param code FileError code
|
||||
* @return string message corresponding to the error code
|
||||
*/
|
||||
StoragePhoneGap.prototype._errorCodeToString = function(code) {
|
||||
switch (code) {
|
||||
case FileError.QUOTA_EXCEEDED_ERR:
|
||||
return 'QUOTA_EXCEEDED_ERR';
|
||||
case FileError.NOT_FOUND_ERR:
|
||||
return 'NOT_FOUND_ERR';
|
||||
case FileError.SECURITY_ERR:
|
||||
return 'SECURITY_ERR';
|
||||
case FileError.INVALID_MODIFICATION_ERR:
|
||||
return 'INVALID_MODIFICATION_ERR';
|
||||
case FileError.INVALID_STATE_ERR:
|
||||
return 'INVALID_STATE_ERR';
|
||||
default:
|
||||
return 'Unknown Error';
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
StorageFirefoxOS: StorageFirefoxOS,
|
||||
StoragePhoneGap: StoragePhoneGap
|
||||
};
|
||||
});
|
@ -113,72 +113,6 @@ define(function(require) {
|
||||
return (r > 0 ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function emulates a "composite" storage enumeration
|
||||
* (i.e. returns files from all the storage areas)
|
||||
* This is needed since the removal of composite storage :
|
||||
* see https://bugzilla.mozilla.org/show_bug.cgi?id=885753
|
||||
*
|
||||
* This code was copied (with only slight modifications) from Gaia source code :
|
||||
* https://bug893282.bugzilla.mozilla.org/attachment.cgi?id=785076
|
||||
*
|
||||
* @param {type} storages List of DeviceStorage instances
|
||||
* @returns {_L22.enumerateAll.cursor} Cursor of files found in device storages
|
||||
*/
|
||||
function enumerateAll(storages) {
|
||||
var storageIndex = 0;
|
||||
var ds_cursor = null;
|
||||
|
||||
var cursor = {
|
||||
continue: function cursor_continue() {
|
||||
ds_cursor.continue();
|
||||
}
|
||||
};
|
||||
|
||||
function enumerateNextStorage() {
|
||||
ds_cursor = storages[storageIndex].enumerate();
|
||||
ds_cursor.onsuccess = onsuccess;
|
||||
ds_cursor.onerror = onerror;
|
||||
};
|
||||
|
||||
function onsuccess(e) {
|
||||
cursor.result = e.target.result;
|
||||
if (!cursor.result) {
|
||||
storageIndex++;
|
||||
if (storageIndex < storages.length) {
|
||||
enumerateNextStorage();
|
||||
return;
|
||||
}
|
||||
// If we've run out of storages, then we fall through and call
|
||||
// onsuccess with the null result.
|
||||
}
|
||||
if (cursor.onsuccess) {
|
||||
try {
|
||||
cursor.onsuccess(e);
|
||||
} catch (err) {
|
||||
console.warn('enumerateAll onsuccess threw', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onerror(e) {
|
||||
cursor.error = e.target.error;
|
||||
if (cursor.onerror) {
|
||||
try {
|
||||
cursor.onerror(e);
|
||||
} catch (err) {
|
||||
console.warn('enumerateAll onerror threw', err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
enumerateNextStorage();
|
||||
return cursor;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Functions and classes exposed by this module
|
||||
*/
|
||||
@ -188,7 +122,6 @@ define(function(require) {
|
||||
readIntegerFrom2Bytes : readIntegerFrom2Bytes,
|
||||
readFloatFrom4Bytes : readFloatFrom4Bytes,
|
||||
uint8ArrayToHex : uint8ArrayToHex,
|
||||
uint8ArrayToBase64 : uint8ArrayToBase64,
|
||||
enumerateAll : enumerateAll
|
||||
uint8ArrayToBase64 : uint8ArrayToBase64
|
||||
};
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user