1
mergeInto(LibraryManager.library, {
2
$IDBFS__deps: ['$FS', '$MEMFS', '$PATH'],
5
indexedDB: function() {
6
return window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
9
DB_STORE_NAME: 'FILE_DATA',
10
// reuse all of the core MEMFS functionality
11
mount: function(mount) {
12
return MEMFS.mount.apply(null, arguments);
14
// the only custom function IDBFS implements is to handle
15
// synchronizing the wrapped MEMFS with a backing IDB instance
16
syncfs: function(mount, populate, callback) {
17
IDBFS.getLocalSet(mount, function(err, local) {
18
if (err) return callback(err);
20
IDBFS.getRemoteSet(mount, function(err, remote) {
21
if (err) return callback(err);
23
var src = populate ? remote : local;
24
var dst = populate ? local : remote;
26
IDBFS.reconcile(src, dst, callback);
30
reconcile: function(src, dst, callback) {
34
for (var key in src.files) {
35
if (!src.files.hasOwnProperty(key)) continue;
36
var e = src.files[key];
37
var e2 = dst.files[key];
38
if (!e2 || e.timestamp > e2.timestamp) {
45
for (var key in dst.files) {
46
if (!dst.files.hasOwnProperty(key)) continue;
47
var e = dst.files[key];
48
var e2 = src.files[key];
57
return callback(null);
62
if (err) return callback(err);
63
if (++completed >= total) {
64
return callback(null);
68
// create a single transaction to handle and IDB reads / writes we'll need to do
69
var db = src.type === 'remote' ? src.db : dst.db;
70
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readwrite');
71
transaction.onerror = function transaction_onerror() { callback(this.error); };
72
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
74
for (var path in create) {
75
if (!create.hasOwnProperty(path)) continue;
76
var entry = create[path];
78
if (dst.type === 'local') {
81
if (FS.isDir(entry.mode)) {
82
FS.mkdir(path, entry.mode);
83
} else if (FS.isFile(entry.mode)) {
84
var stream = FS.open(path, 'w+', 0666);
85
FS.write(stream, entry.contents, 0, entry.contents.length, 0, true /* canOwn */);
94
var req = store.put(entry, path);
95
req.onsuccess = function req_onsuccess() { done(null); };
96
req.onerror = function req_onerror() { done(this.error); };
100
for (var path in remove) {
101
if (!remove.hasOwnProperty(path)) continue;
102
var entry = remove[path];
104
if (dst.type === 'local') {
105
// delete file from local
107
if (FS.isDir(entry.mode)) {
108
// TODO recursive delete?
110
} else if (FS.isFile(entry.mode)) {
118
// delete file from IDB
119
var req = store.delete(path);
120
req.onsuccess = function req_onsuccess() { done(null); };
121
req.onerror = function req_onerror() { done(this.error); };
125
getLocalSet: function(mount, callback) {
128
function isRealDir(p) {
129
return p !== '.' && p !== '..';
131
function toAbsolute(root) {
133
return PATH.join2(root, p);
137
var check = FS.readdir(mount.mountpoint)
139
.map(toAbsolute(mount.mountpoint));
141
while (check.length) {
142
var path = check.pop();
146
var lookup = FS.lookupPath(path);
148
stat = FS.stat(path);
153
if (FS.isDir(stat.mode)) {
154
check.push.apply(check, FS.readdir(path)
156
.map(toAbsolute(path)));
158
files[path] = { mode: stat.mode, timestamp: stat.mtime };
159
} else if (FS.isFile(stat.mode)) {
160
files[path] = { contents: node.contents, mode: stat.mode, timestamp: stat.mtime };
162
return callback(new Error('node type not supported'));
166
return callback(null, { type: 'local', files: files });
168
getDB: function(name, callback) {
169
// look it up in the cache
170
var db = IDBFS.dbs[name];
172
return callback(null, db);
176
req = IDBFS.indexedDB().open(name, IDBFS.DB_VERSION);
180
req.onupgradeneeded = function req_onupgradeneeded() {
182
db.createObjectStore(IDBFS.DB_STORE_NAME);
184
req.onsuccess = function req_onsuccess() {
187
IDBFS.dbs[name] = db;
190
req.onerror = function req_onerror() {
191
callback(this.error);
194
getRemoteSet: function(mount, callback) {
197
IDBFS.getDB(mount.mountpoint, function(err, db) {
198
if (err) return callback(err);
200
var transaction = db.transaction([IDBFS.DB_STORE_NAME], 'readonly');
201
transaction.onerror = function transaction_onerror() { callback(this.error); };
203
var store = transaction.objectStore(IDBFS.DB_STORE_NAME);
204
store.openCursor().onsuccess = function store_openCursor_onsuccess(event) {
205
var cursor = event.target.result;
207
return callback(null, { type: 'remote', db: db, files: files });
210
files[cursor.key] = cursor.value;