openEventsPlatformApp/plugins/cordova-plugin-file/src/firefoxos/FileProxy.js
plugins/cordova-plugin-file/src/firefoxos/FileProxy.jsRaw
1/*
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 */
21
22/* global IDBKeyRange */
23
24var LocalFileSystem = require('./LocalFileSystem'),
25 FileSystem = require('./FileSystem'),
26 FileEntry = require('./FileEntry'),
27 FileError = require('./FileError'),
28 DirectoryEntry = require('./DirectoryEntry'),
29 File = require('./File');
30
31/*
32QUIRKS:
33 Does not fail when removing non-empty directories
34 Does not support metadata for directories
35 Does not support requestAllFileSystems
36 Does not support resolveLocalFileSystemURI
37 Methods copyTo and moveTo do not support directories
38
39 Heavily based on https://github.com/ebidel/idb.filesystem.js
40 */
41
42
43(function(exports, global) {
44 var indexedDB = global.indexedDB || global.mozIndexedDB;
45 if (!indexedDB) {
46 throw "Firefox OS File plugin: indexedDB not supported";
47 }
48
49 var fs_ = null;
50
51 var idb_ = {};
52 idb_.db = null;
53 var FILE_STORE_ = 'entries';
54
55 var DIR_SEPARATOR = '/';
56 var DIR_OPEN_BOUND = String.fromCharCode(DIR_SEPARATOR.charCodeAt(0) + 1);
57
58 var pathsPrefix = {
59 // Read-only directory where the application is installed.
60 applicationDirectory: location.origin + "/",
61 // Where to put app-specific data files.
62 dataDirectory: 'file:///persistent/',
63 // Cached files that should survive app restarts.
64 // Apps should not rely on the OS to delete files in here.
65 cacheDirectory: 'file:///temporary/',
66 };
67
68/*** Exported functionality ***/
69
70 exports.requestFileSystem = function(successCallback, errorCallback, args) {
71 var type = args[0];
72 //var size = args[1];
73
74 if (type !== LocalFileSystem.TEMPORARY && type !== LocalFileSystem.PERSISTENT) {
75 if (errorCallback) {
76 errorCallback(FileError.INVALID_MODIFICATION_ERR);
77 }
78 return;
79 }
80
81 var name = type === LocalFileSystem.TEMPORARY ? 'temporary' : 'persistent';
82 var storageName = (location.protocol + location.host).replace(/:/g, '_');
83
84 var root = new DirectoryEntry('', DIR_SEPARATOR);
85 fs_ = new FileSystem(name, root);
86
87 idb_.open(storageName, function() {
88 successCallback(fs_);
89 }, errorCallback);
90 };
91
92 require('./fileSystems').getFs = function(name, callback) {
93 callback(new FileSystem(name, fs_.root));
94 };
95
96 // list a directory's contents (files and folders).
97 exports.readEntries = function(successCallback, errorCallback, args) {
98 var fullPath = args[0];
99
100 if (!successCallback) {
101 throw Error('Expected successCallback argument.');
102 }
103
104 var path = resolveToFullPath_(fullPath);
105
106 idb_.getAllEntries(path.fullPath, path.storagePath, function(entries) {
107 successCallback(entries);
108 }, errorCallback);
109 };
110
111 exports.getFile = function(successCallback, errorCallback, args) {
112 var fullPath = args[0];
113 var path = args[1];
114 var options = args[2] || {};
115
116 // Create an absolute path if we were handed a relative one.
117 path = resolveToFullPath_(fullPath, path);
118
119 idb_.get(path.storagePath, function(fileEntry) {
120 if (options.create === true && options.exclusive === true && fileEntry) {
121 // If create and exclusive are both true, and the path already exists,
122 // getFile must fail.
123
124 if (errorCallback) {
125 errorCallback(FileError.PATH_EXISTS_ERR);
126 }
127 } else if (options.create === true && !fileEntry) {
128 // If create is true, the path doesn't exist, and no other error occurs,
129 // getFile must create it as a zero-length file and return a corresponding
130 // FileEntry.
131 var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
132
133 newFileEntry.file_ = new MyFile({
134 size: 0,
135 name: newFileEntry.name,
136 lastModifiedDate: new Date(),
137 storagePath: path.storagePath
138 });
139
140 idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback);
141 } else if (options.create === true && fileEntry) {
142 if (fileEntry.isFile) {
143 // Overwrite file, delete then create new.
144 idb_['delete'](path.storagePath, function() {
145 var newFileEntry = new FileEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
146
147 newFileEntry.file_ = new MyFile({
148 size: 0,
149 name: newFileEntry.name,
150 lastModifiedDate: new Date(),
151 storagePath: path.storagePath
152 });
153
154 idb_.put(newFileEntry, path.storagePath, successCallback, errorCallback);
155 }, errorCallback);
156 } else {
157 if (errorCallback) {
158 errorCallback(FileError.INVALID_MODIFICATION_ERR);
159 }
160 }
161 } else if ((!options.create || options.create === false) && !fileEntry) {
162 // If create is not true and the path doesn't exist, getFile must fail.
163 if (errorCallback) {
164 errorCallback(FileError.NOT_FOUND_ERR);
165 }
166 } else if ((!options.create || options.create === false) && fileEntry &&
167 fileEntry.isDirectory) {
168 // If create is not true and the path exists, but is a directory, getFile
169 // must fail.
170 if (errorCallback) {
171 errorCallback(FileError.INVALID_MODIFICATION_ERR);
172 }
173 } else {
174 // Otherwise, if no other error occurs, getFile must return a FileEntry
175 // corresponding to path.
176
177 successCallback(fileEntryFromIdbEntry(fileEntry));
178 }
179 }, errorCallback);
180 };
181
182 exports.getFileMetadata = function(successCallback, errorCallback, args) {
183 var fullPath = args[0];
184
185 exports.getFile(function(fileEntry) {
186 successCallback(new File(fileEntry.file_.name, fileEntry.fullPath, '', fileEntry.file_.lastModifiedDate,
187 fileEntry.file_.size));
188 }, errorCallback, [fullPath, null]);
189 };
190
191 exports.getMetadata = function(successCallback, errorCallback, args) {
192 exports.getFile(function (fileEntry) {
193 successCallback(
194 {
195 modificationTime: fileEntry.file_.lastModifiedDate,
196 size: fileEntry.file_.lastModifiedDate
197 });
198 }, errorCallback, args);
199 };
200
201 exports.setMetadata = function(successCallback, errorCallback, args) {
202 var fullPath = args[0];
203 var metadataObject = args[1];
204
205 exports.getFile(function (fileEntry) {
206 fileEntry.file_.lastModifiedDate = metadataObject.modificationTime;
207 }, errorCallback, [fullPath, null]);
208 };
209
210 exports.write = function(successCallback, errorCallback, args) {
211 var fileName = args[0],
212 data = args[1],
213 position = args[2];
214 //isBinary = args[3];
215
216 if (!data) {
217 if (errorCallback) {
218 errorCallback(FileError.INVALID_MODIFICATION_ERR);
219 }
220 return;
221 }
222
223 exports.getFile(function(fileEntry) {
224 var blob_ = fileEntry.file_.blob_;
225
226 if (!blob_) {
227 blob_ = new Blob([data], {type: data.type});
228 } else {
229 // Calc the head and tail fragments
230 var head = blob_.slice(0, position);
231 var tail = blob_.slice(position + data.byteLength);
232
233 // Calc the padding
234 var padding = position - head.size;
235 if (padding < 0) {
236 padding = 0;
237 }
238
239 // Do the "write". In fact, a full overwrite of the Blob.
240 blob_ = new Blob([head, new Uint8Array(padding), data, tail],
241 {type: data.type});
242 }
243
244 // Set the blob we're writing on this file entry so we can recall it later.
245 fileEntry.file_.blob_ = blob_;
246 fileEntry.file_.lastModifiedDate = data.lastModifiedDate || null;
247 fileEntry.file_.size = blob_.size;
248 fileEntry.file_.name = blob_.name;
249 fileEntry.file_.type = blob_.type;
250
251 idb_.put(fileEntry, fileEntry.file_.storagePath, function() {
252 successCallback(data.byteLength);
253 }, errorCallback);
254 }, errorCallback, [fileName, null]);
255 };
256
257 exports.readAsText = function(successCallback, errorCallback, args) {
258 var fileName = args[0],
259 enc = args[1],
260 startPos = args[2],
261 endPos = args[3];
262
263 readAs('text', fileName, enc, startPos, endPos, successCallback, errorCallback);
264 };
265
266 exports.readAsDataURL = function(successCallback, errorCallback, args) {
267 var fileName = args[0],
268 startPos = args[1],
269 endPos = args[2];
270
271 readAs('dataURL', fileName, null, startPos, endPos, successCallback, errorCallback);
272 };
273
274 exports.readAsBinaryString = function(successCallback, errorCallback, args) {
275 var fileName = args[0],
276 startPos = args[1],
277 endPos = args[2];
278
279 readAs('binaryString', fileName, null, startPos, endPos, successCallback, errorCallback);
280 };
281
282 exports.readAsArrayBuffer = function(successCallback, errorCallback, args) {
283 var fileName = args[0],
284 startPos = args[1],
285 endPos = args[2];
286
287 readAs('arrayBuffer', fileName, null, startPos, endPos, successCallback, errorCallback);
288 };
289
290 exports.removeRecursively = exports.remove = function(successCallback, errorCallback, args) {
291 var fullPath = args[0];
292
293 // TODO: This doesn't protect against directories that have content in it.
294 // Should throw an error instead if the dirEntry is not empty.
295 idb_['delete'](fullPath, function() {
296 successCallback();
297 }, errorCallback);
298 };
299
300 exports.getDirectory = function(successCallback, errorCallback, args) {
301 var fullPath = args[0];
302 var path = args[1];
303 var options = args[2];
304
305 // Create an absolute path if we were handed a relative one.
306 path = resolveToFullPath_(fullPath, path);
307
308 idb_.get(path.storagePath, function(folderEntry) {
309 if (!options) {
310 options = {};
311 }
312
313 if (options.create === true && options.exclusive === true && folderEntry) {
314 // If create and exclusive are both true, and the path already exists,
315 // getDirectory must fail.
316 if (errorCallback) {
317 errorCallback(FileError.INVALID_MODIFICATION_ERR);
318 }
319 } else if (options.create === true && !folderEntry) {
320 // If create is true, the path doesn't exist, and no other error occurs,
321 // getDirectory must create it as a zero-length file and return a corresponding
322 // MyDirectoryEntry.
323 var dirEntry = new DirectoryEntry(path.fileName, path.fullPath, new FileSystem(path.fsName, fs_.root));
324
325 idb_.put(dirEntry, path.storagePath, successCallback, errorCallback);
326 } else if (options.create === true && folderEntry) {
327
328 if (folderEntry.isDirectory) {
329 // IDB won't save methods, so we need re-create the MyDirectoryEntry.
330 successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.fileSystem));
331 } else {
332 if (errorCallback) {
333 errorCallback(FileError.INVALID_MODIFICATION_ERR);
334 }
335 }
336 } else if ((!options.create || options.create === false) && !folderEntry) {
337 // Handle root special. It should always exist.
338 if (path.fullPath === DIR_SEPARATOR) {
339 successCallback(fs_.root);
340 return;
341 }
342
343 // If create is not true and the path doesn't exist, getDirectory must fail.
344 if (errorCallback) {
345 errorCallback(FileError.NOT_FOUND_ERR);
346 }
347 } else if ((!options.create || options.create === false) && folderEntry &&
348 folderEntry.isFile) {
349 // If create is not true and the path exists, but is a file, getDirectory
350 // must fail.
351 if (errorCallback) {
352 errorCallback(FileError.INVALID_MODIFICATION_ERR);
353 }
354 } else {
355 // Otherwise, if no other error occurs, getDirectory must return a
356 // MyDirectoryEntry corresponding to path.
357
358 // IDB won't' save methods, so we need re-create MyDirectoryEntry.
359 successCallback(new DirectoryEntry(folderEntry.name, folderEntry.fullPath, folderEntry.fileSystem));
360 }
361 }, errorCallback);
362 };
363
364 exports.getParent = function(successCallback, errorCallback, args) {
365 var fullPath = args[0];
366
367 if (fullPath === DIR_SEPARATOR) {
368 successCallback(fs_.root);
369 return;
370 }
371
372 var pathArr = fullPath.split(DIR_SEPARATOR);
373 pathArr.pop();
374 var namesa = pathArr.pop();
375 var path = pathArr.join(DIR_SEPARATOR);
376
377 exports.getDirectory(successCallback, errorCallback, [path, namesa, {create: false}]);
378 };
379
380 exports.copyTo = function(successCallback, errorCallback, args) {
381 var srcPath = args[0];
382 var parentFullPath = args[1];
383 var name = args[2];
384
385 // Read src file
386 exports.getFile(function(srcFileEntry) {
387
388 // Create dest file
389 exports.getFile(function(dstFileEntry) {
390
391 exports.write(function() {
392 successCallback(dstFileEntry);
393 }, errorCallback, [dstFileEntry.file_.storagePath, srcFileEntry.file_.blob_, 0]);
394
395 }, errorCallback, [parentFullPath, name, {create: true}]);
396
397 }, errorCallback, [srcPath, null]);
398 };
399
400 exports.moveTo = function(successCallback, errorCallback, args) {
401 var srcPath = args[0];
402 //var parentFullPath = args[1];
403 //var name = args[2];
404
405 exports.copyTo(function (fileEntry) {
406
407 exports.remove(function () {
408 successCallback(fileEntry);
409 }, errorCallback, [srcPath]);
410
411 }, errorCallback, args);
412 };
413
414 exports.resolveLocalFileSystemURI = function(successCallback, errorCallback, args) {
415 var path = args[0];
416
417 // Ignore parameters
418 if (path.indexOf('?') !== -1) {
419 path = String(path).split("?")[0];
420 }
421
422 // support for encodeURI
423 if (/\%5/g.test(path)) {
424 path = decodeURI(path);
425 }
426
427 if (path.indexOf(pathsPrefix.dataDirectory) === 0) {
428 path = path.substring(pathsPrefix.dataDirectory.length - 1);
429
430 exports.requestFileSystem(function(fs) {
431 fs.root.getFile(path, {create: false}, successCallback, function() {
432 fs.root.getDirectory(path, {create: false}, successCallback, errorCallback);
433 });
434 }, errorCallback, [LocalFileSystem.PERSISTENT]);
435 } else if (path.indexOf(pathsPrefix.cacheDirectory) === 0) {
436 path = path.substring(pathsPrefix.cacheDirectory.length - 1);
437
438 exports.requestFileSystem(function(fs) {
439 fs.root.getFile(path, {create: false}, successCallback, function() {
440 fs.root.getDirectory(path, {create: false}, successCallback, errorCallback);
441 });
442 }, errorCallback, [LocalFileSystem.TEMPORARY]);
443 } else if (path.indexOf(pathsPrefix.applicationDirectory) === 0) {
444 path = path.substring(pathsPrefix.applicationDirectory.length);
445
446 var xhr = new XMLHttpRequest();
447 xhr.open("GET", path, true);
448 xhr.onreadystatechange = function () {
449 if (xhr.status === 200 && xhr.readyState === 4) {
450 exports.requestFileSystem(function(fs) {
451 fs.name = location.hostname;
452 fs.root.getFile(path, {create: true}, writeFile, errorCallback);
453 }, errorCallback, [LocalFileSystem.PERSISTENT]);
454 }
455 };
456
457 xhr.onerror = function () {
458 if (errorCallback) {
459 errorCallback(FileError.NOT_READABLE_ERR);
460 }
461 };
462
463 xhr.send();
464 } else {
465 if (errorCallback) {
466 errorCallback(FileError.NOT_FOUND_ERR);
467 }
468 }
469
470 function writeFile(entry) {
471 entry.createWriter(function (fileWriter) {
472 fileWriter.onwriteend = function (evt) {
473 if (!evt.target.error) {
474 entry.filesystemName = location.hostname;
475 successCallback(entry);
476 }
477 };
478 fileWriter.onerror = function () {
479 if (errorCallback) {
480 errorCallback(FileError.NOT_READABLE_ERR);
481 }
482 };
483 fileWriter.write(new Blob([xhr.response]));
484 }, errorCallback);
485 }
486 };
487
488 exports.requestAllPaths = function(successCallback) {
489 successCallback(pathsPrefix);
490 };
491
492/*** Helpers ***/
493
494 /**
495 * Interface to wrap the native File interface.
496 *
497 * This interface is necessary for creating zero-length (empty) files,
498 * something the Filesystem API allows you to do. Unfortunately, File's
499 * constructor cannot be called directly, making it impossible to instantiate
500 * an empty File in JS.
501 *
502 * @param {Object} opts Initial values.
503 * @constructor
504 */
505 function MyFile(opts) {
506 var blob_ = new Blob();
507
508 this.size = opts.size || 0;
509 this.name = opts.name || '';
510 this.type = opts.type || '';
511 this.lastModifiedDate = opts.lastModifiedDate || null;
512 this.storagePath = opts.storagePath || '';
513
514 // Need some black magic to correct the object's size/name/type based on the
515 // blob that is saved.
516 Object.defineProperty(this, 'blob_', {
517 enumerable: true,
518 get: function() {
519 return blob_;
520 },
521 set: function(val) {
522 blob_ = val;
523 this.size = blob_.size;
524 this.name = blob_.name;
525 this.type = blob_.type;
526 this.lastModifiedDate = blob_.lastModifiedDate;
527 }.bind(this)
528 });
529 }
530
531 MyFile.prototype.constructor = MyFile;
532
533 // When saving an entry, the fullPath should always lead with a slash and never
534 // end with one (e.g. a directory). Also, resolve '.' and '..' to an absolute
535 // one. This method ensures path is legit!
536 function resolveToFullPath_(cwdFullPath, path) {
537 path = path || '';
538 var fullPath = path;
539 var prefix = '';
540
541 cwdFullPath = cwdFullPath || DIR_SEPARATOR;
542 if (cwdFullPath.indexOf(FILESYSTEM_PREFIX) === 0) {
543 prefix = cwdFullPath.substring(0, cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length));
544 cwdFullPath = cwdFullPath.substring(cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length));
545 }
546
547 var relativePath = path[0] !== DIR_SEPARATOR;
548 if (relativePath) {
549 fullPath = cwdFullPath;
550 if (cwdFullPath != DIR_SEPARATOR) {
551 fullPath += DIR_SEPARATOR + path;
552 } else {
553 fullPath += path;
554 }
555 }
556
557 // Adjust '..'s by removing parent directories when '..' flows in path.
558 var parts = fullPath.split(DIR_SEPARATOR);
559 for (var i = 0; i < parts.length; ++i) {
560 var part = parts[i];
561 if (part == '..') {
562 parts[i - 1] = '';
563 parts[i] = '';
564 }
565 }
566 fullPath = parts.filter(function(el) {
567 return el;
568 }).join(DIR_SEPARATOR);
569
570 // Add back in leading slash.
571 if (fullPath[0] !== DIR_SEPARATOR) {
572 fullPath = DIR_SEPARATOR + fullPath;
573 }
574
575 // Replace './' by current dir. ('./one/./two' -> one/two)
576 fullPath = fullPath.replace(/\.\//g, DIR_SEPARATOR);
577
578 // Replace '//' with '/'.
579 fullPath = fullPath.replace(/\/\//g, DIR_SEPARATOR);
580
581 // Replace '/.' with '/'.
582 fullPath = fullPath.replace(/\/\./g, DIR_SEPARATOR);
583
584 // Remove '/' if it appears on the end.
585 if (fullPath[fullPath.length - 1] == DIR_SEPARATOR &&
586 fullPath != DIR_SEPARATOR) {
587 fullPath = fullPath.substring(0, fullPath.length - 1);
588 }
589
590 return {
591 storagePath: prefix + fullPath,
592 fullPath: fullPath,
593 fileName: fullPath.split(DIR_SEPARATOR).pop(),
594 fsName: prefix.split(DIR_SEPARATOR).pop()
595 };
596 }
597
598 function fileEntryFromIdbEntry(fileEntry) {
599 // IDB won't save methods, so we need re-create the FileEntry.
600 var clonedFileEntry = new FileEntry(fileEntry.name, fileEntry.fullPath, fileEntry.fileSystem);
601 clonedFileEntry.file_ = fileEntry.file_;
602
603 return clonedFileEntry;
604 }
605
606 function readAs(what, fullPath, encoding, startPos, endPos, successCallback, errorCallback) {
607 exports.getFile(function(fileEntry) {
608 var fileReader = new FileReader(),
609 blob = fileEntry.file_.blob_.slice(startPos, endPos);
610
611 fileReader.onload = function(e) {
612 successCallback(e.target.result);
613 };
614
615 fileReader.onerror = errorCallback;
616
617 switch (what) {
618 case 'text':
619 fileReader.readAsText(blob, encoding);
620 break;
621 case 'dataURL':
622 fileReader.readAsDataURL(blob);
623 break;
624 case 'arrayBuffer':
625 fileReader.readAsArrayBuffer(blob);
626 break;
627 case 'binaryString':
628 fileReader.readAsBinaryString(blob);
629 break;
630 }
631
632 }, errorCallback, [fullPath, null]);
633 }
634
635/*** Core logic to handle IDB operations ***/
636
637 idb_.open = function(dbName, successCallback, errorCallback) {
638 var self = this;
639
640 // TODO: FF 12.0a1 isn't liking a db name with : in it.
641 var request = indexedDB.open(dbName.replace(':', '_')/*, 1 /*version*/);
642
643 request.onerror = errorCallback || onError;
644
645 request.onupgradeneeded = function(e) {
646 // First open was called or higher db version was used.
647
648 // console.log('onupgradeneeded: oldVersion:' + e.oldVersion,
649 // 'newVersion:' + e.newVersion);
650
651 self.db = e.target.result;
652 self.db.onerror = onError;
653
654 if (!self.db.objectStoreNames.contains(FILE_STORE_)) {
655 self.db.createObjectStore(FILE_STORE_/*,{keyPath: 'id', autoIncrement: true}*/);
656 }
657 };
658
659 request.onsuccess = function(e) {
660 self.db = e.target.result;
661 self.db.onerror = onError;
662 successCallback(e);
663 };
664
665 request.onblocked = errorCallback || onError;
666 };
667
668 idb_.close = function() {
669 this.db.close();
670 this.db = null;
671 };
672
673 idb_.get = function(fullPath, successCallback, errorCallback) {
674 if (!this.db) {
675 if (errorCallback) {
676 errorCallback(FileError.INVALID_MODIFICATION_ERR);
677 }
678 return;
679 }
680
681 var tx = this.db.transaction([FILE_STORE_], 'readonly');
682
683 //var request = tx.objectStore(FILE_STORE_).get(fullPath);
684 var range = IDBKeyRange.bound(fullPath, fullPath + DIR_OPEN_BOUND,
685 false, true);
686 var request = tx.objectStore(FILE_STORE_).get(range);
687
688 tx.onabort = errorCallback || onError;
689 tx.oncomplete = function(e) {
690 successCallback(request.result);
691 };
692 };
693
694 idb_.getAllEntries = function(fullPath, storagePath, successCallback, errorCallback) {
695 if (!this.db) {
696 if (errorCallback) {
697 errorCallback(FileError.INVALID_MODIFICATION_ERR);
698 }
699 return;
700 }
701
702 var results = [];
703
704 if (storagePath[storagePath.length - 1] === DIR_SEPARATOR) {
705 storagePath = storagePath.substring(0, storagePath.length - 1);
706 }
707
708 var range = IDBKeyRange.bound(
709 storagePath + DIR_SEPARATOR, storagePath + DIR_OPEN_BOUND, false, true);
710
711 var tx = this.db.transaction([FILE_STORE_], 'readonly');
712 tx.onabort = errorCallback || onError;
713 tx.oncomplete = function(e) {
714 results = results.filter(function(val) {
715 var valPartsLen = val.fullPath.split(DIR_SEPARATOR).length;
716 var fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length;
717
718 if (fullPath === DIR_SEPARATOR && valPartsLen < fullPathPartsLen + 1) {
719 // Hack to filter out entries in the root folder. This is inefficient
720 // because reading the entires of fs.root (e.g. '/') returns ALL
721 // results in the database, then filters out the entries not in '/'.
722 return val;
723 } else if (fullPath !== DIR_SEPARATOR &&
724 valPartsLen === fullPathPartsLen + 1) {
725 // If this a subfolder and entry is a direct child, include it in
726 // the results. Otherwise, it's not an entry of this folder.
727 return val;
728 }
729 });
730
731 successCallback(results);
732 };
733
734 var request = tx.objectStore(FILE_STORE_).openCursor(range);
735
736 request.onsuccess = function(e) {
737 var cursor = e.target.result;
738 if (cursor) {
739 var val = cursor.value;
740
741 results.push(val.isFile ? fileEntryFromIdbEntry(val) : new DirectoryEntry(val.name, val.fullPath, val.fileSystem));
742 cursor['continue']();
743 }
744 };
745 };
746
747 idb_['delete'] = function(fullPath, successCallback, errorCallback) {
748 if (!this.db) {
749 if (errorCallback) {
750 errorCallback(FileError.INVALID_MODIFICATION_ERR);
751 }
752 return;
753 }
754
755 var tx = this.db.transaction([FILE_STORE_], 'readwrite');
756 tx.oncomplete = successCallback;
757 tx.onabort = errorCallback || onError;
758
759 //var request = tx.objectStore(FILE_STORE_).delete(fullPath);
760 var range = IDBKeyRange.bound(
761 fullPath, fullPath + DIR_OPEN_BOUND, false, true);
762 tx.objectStore(FILE_STORE_)['delete'](range);
763 };
764
765 idb_.put = function(entry, storagePath, successCallback, errorCallback) {
766 if (!this.db) {
767 if (errorCallback) {
768 errorCallback(FileError.INVALID_MODIFICATION_ERR);
769 }
770 return;
771 }
772
773 var tx = this.db.transaction([FILE_STORE_], 'readwrite');
774 tx.onabort = errorCallback || onError;
775 tx.oncomplete = function(e) {
776 // TODO: Error is thrown if we pass the request event back instead.
777 successCallback(entry);
778 };
779
780 tx.objectStore(FILE_STORE_).put(entry, storagePath);
781 };
782
783 // Global error handler. Errors bubble from request, to transaction, to db.
784 function onError(e) {
785 switch (e.target.errorCode) {
786 case 12:
787 console.log('Error - Attempt to open db with a lower version than the ' +
788 'current one.');
789 break;
790 default:
791 console.log('errorCode: ' + e.target.errorCode);
792 }
793
794 console.log(e, e.code, e.message);
795 }
796
797// Clean up.
798// TODO: Is there a place for this?
799// global.addEventListener('beforeunload', function(e) {
800// idb_.db && idb_.db.close();
801// }, false);
802
803})(module.exports, window);
804
805require("cordova/exec/proxy").add("File", module.exports);