Repository: cordova-plugin-file
Updated Branches:
  refs/heads/master 28662835e -> 282fa0983


Support for resolve URI, request all paths and local app directory.


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/commit/98fed5f7
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/tree/98fed5f7
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/diff/98fed5f7

Branch: refs/heads/master
Commit: 98fed5f788f33c698e6a5bedb75b20a232641115
Parents: 429d072
Author: Rodrigo Silveira <[email protected]>
Authored: Wed Jul 9 15:46:50 2014 -0700
Committer: Rodrigo Silveira <[email protected]>
Committed: Tue Jul 22 15:13:33 2014 -0700

----------------------------------------------------------------------
 doc/index.md                |   7 +-
 plugin.xml                  |  10 ++
 src/firefoxos/FileProxy.js  | 221 +++++++++++++++++++++++++++++----------
 www/firefoxos/FileSystem.js |  29 +++++
 4 files changed, 210 insertions(+), 57 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/98fed5f7/doc/index.md
----------------------------------------------------------------------
diff --git a/doc/index.md b/doc/index.md
index 504b493..875e913 100644
--- a/doc/index.md
+++ b/doc/index.md
@@ -153,9 +153,14 @@ as a shim on top of indexedDB.
  
 * Does not fail when removing non-empty directories
 * Does not support metadata for directories
-* Does not support `requestAllFileSystems` and `resolveLocalFileSystemURI` 
methods
 * Methods `copyTo` and `moveTo` do not support directories
 
+The following data paths are supported:
+* `applicationDirectory` - Uses `xhr` to get local files that are packaged 
with the app.
+* `dataDirectory` - For persistent app-specific data files.
+* `cacheDirectory` - Cached files that should survive app restarts (Apps 
should not rely
+on the OS to delete files in here).
+
 ## Upgrading Notes
 
 In v1.0.0 of this plugin, the `FileEntry` and `DirectoryEntry` structures have 
changed,

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/98fed5f7/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
index 4e1c233..0da51ed 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -313,6 +313,16 @@ xmlns:android="http://schemas.android.com/apk/res/android";
         <js-module src="src/firefoxos/FileProxy.js" name="FileProxy">
             <runs />
         </js-module>
+
+        <js-module src="www/fileSystemPaths.js" name="fileSystemPaths">
+            <merges target="cordova" />
+            <runs/>
+        </js-module>
+
+        <!-- Firefox OS specific file apis -->
+        <js-module src="www/firefoxos/FileSystem.js" name="firefoxFileSystem">
+            <merges target="window.FileSystem" />
+        </js-module>
     </platform>
 
 </plugin>

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/98fed5f7/src/firefoxos/FileProxy.js
----------------------------------------------------------------------
diff --git a/src/firefoxos/FileProxy.js b/src/firefoxos/FileProxy.js
index 8f36632..81cfed7 100644
--- a/src/firefoxos/FileProxy.js
+++ b/src/firefoxos/FileProxy.js
@@ -46,7 +46,6 @@ QUIRKS:
 
     var fs_ = null;
 
-    var storageType_ = 'temporary';
     var idb_ = {};
     idb_.db = null;
     var FILE_STORE_ = 'entries';
@@ -54,6 +53,32 @@ QUIRKS:
     var DIR_SEPARATOR = '/';
     var DIR_OPEN_BOUND = String.fromCharCode(DIR_SEPARATOR.charCodeAt(0) + 1);
 
+    var pathsPrefix = {
+        // Read-only directory where the application is installed.
+        applicationDirectory: location.origin + "/",
+        // Root of app's private writable storage
+        applicationStorageDirectory: null,
+        // Where to put app-specific data files.
+        dataDirectory: 'file:///persistent/',
+        // Cached files that should survive app restarts.
+        // Apps should not rely on the OS to delete files in here.
+        cacheDirectory: 'file:///temporary/',
+        // Android: the application space on external storage.
+        externalApplicationStorageDirectory: null,
+        // Android: Where to put app-specific data files on external storage.
+        externalDataDirectory: null,
+        // Android: the application cache on external storage.
+        externalCacheDirectory: null,
+        // Android: the external storage (SD card) root.
+        externalRootDirectory: null,
+        // iOS: Temp directory that the OS can clear at will.
+        tempDirectory: null,
+        // iOS: Holds app-specific files that should be synced (e.g. to 
iCloud).
+        syncedDataDirectory: null,
+        // iOS: Files private to the app, but that are meaningful to other 
applciations (e.g. Office files)
+        documentsDirectory: null
+    };
+
 /*** Exported functionality ***/
 
     exports.requestFileSystem = function(successCallback, errorCallback, args) 
{
@@ -61,24 +86,25 @@ QUIRKS:
         var size = args[1];
 
         if (type !== LocalFileSystem.TEMPORARY && type !== 
LocalFileSystem.PERSISTENT) {
-            if (errorCallback) {
-                errorCallback(FileError.INVALID_MODIFICATION_ERR);
-                return;
-            }
+            errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR);
+            return;
         }
 
-        storageType_ = type == LocalFileSystem.TEMPORARY ? 'Temporary' : 
'Persistent';
-        var name = (location.protocol + location.host).replace(/:/g, '_') +
-            ':' + storageType_;
+        var name = type === LocalFileSystem.TEMPORARY ? 'temporary' : 
'persistent';
+        var storageName = (location.protocol + location.host).replace(/:/g, 
'_');
 
         var root = new DirectoryEntry('', DIR_SEPARATOR);
-        var fs_ = new FileSystem(name, root);
+        fs_ = new FileSystem(name, root);
 
-        idb_.open(fs_.name, function() {
+        idb_.open(storageName, function() {
             successCallback(fs_);
         }, errorCallback);
     };
 
+    require('./fileSystems').getFs = function(name, callback) {
+        callback(new FileSystem(name, fs_.root));
+    };
+
     // list a directory's contents (files and folders).
     exports.readEntries = function(successCallback, errorCallback, args) {
         var fullPath = args[0];
@@ -87,20 +113,22 @@ QUIRKS:
             throw Error('Expected successCallback argument.');
         }
 
-        idb_.getAllEntries(fullPath, function(entries) {
+        var path = resolveToFullPath_(fullPath);
+
+        idb_.getAllEntries(path.fullPath, path.storagePath, function(entries) {
             successCallback(entries);
         }, errorCallback);
     };
 
     exports.getFile = function(successCallback, errorCallback, args) {
-        var fullpath = args[0];
+        var fullPath = args[0];
         var path = args[1];
         var options = args[2] || {};
 
         // Create an absolute path if we were handed a relative one.
-        path = resolveToFullPath_(fullpath, path);
+        path = resolveToFullPath_(fullPath, path);
 
-        idb_.get(path, function(fileEntry) {
+        idb_.get(path.storagePath, function(fileEntry) {
             if (options.create === true && options.exclusive === true && 
fileEntry) {
                 // If create and exclusive are both true, and the path already 
exists,
                 // getFile must fail.
@@ -112,16 +140,16 @@ QUIRKS:
                 // If create is true, the path doesn't exist, and no other 
error occurs,
                 // getFile must create it as a zero-length file and return a 
corresponding
                 // FileEntry.
-                var name = path.split(DIR_SEPARATOR).pop(); // Just need 
filename.
-                var newFileEntry = new FileEntry(name, path, fs_);
+                var newFileEntry = new FileEntry(path.fileName, path.fullPath, 
new FileSystem(path.fsName, fs_.root));
 
                 newFileEntry.file_ = new MyFile({
                     size: 0,
                     name: newFileEntry.name,
-                    lastModifiedDate: new Date()
+                    lastModifiedDate: new Date(),
+                    storagePath: path.storagePath
                 });
 
-                idb_.put(newFileEntry, successCallback, errorCallback);
+                idb_.put(newFileEntry, path.storagePath, successCallback, 
errorCallback);
             } else if (options.create === true && fileEntry) {
                 if (fileEntry.isFile) {
                     successCallback(fileEntryFromIdbEntry(fileEntry));
@@ -157,7 +185,7 @@ QUIRKS:
         exports.getFile(function(fileEntry) {
             successCallback(new File(fileEntry.file_.name, fileEntry.fullPath, 
'', fileEntry.file_.lastModifiedDate,
                 fileEntry.file_.size));
-        }, errorCallback, [null, fullPath]);
+        }, errorCallback, [fullPath, null]);
     };
 
     exports.getMetadata = function(successCallback, errorCallback, args) {
@@ -176,7 +204,7 @@ QUIRKS:
 
         exports.getFile(function (fileEntry) {
               fileEntry.file_.lastModifiedDate = 
metadataObject.modificationTime;
-        }, errorCallback, [null, fullPath]);
+        }, errorCallback, [fullPath, null]);
     };
 
     exports.write = function(successCallback, errorCallback, args) {
@@ -186,7 +214,7 @@ QUIRKS:
             isBinary = args[3];
 
         if (!data) {
-            errorCallback(FileError.INVALID_MODIFICATION_ERR);
+            errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR);
             return;
         }
 
@@ -218,10 +246,10 @@ QUIRKS:
             fileEntry.file_.name = blob_.name;
             fileEntry.file_.type = blob_.type;
 
-            idb_.put(fileEntry, function() {
+            idb_.put(fileEntry, fileEntry.file_.storagePath, function() {
                 successCallback(data.byteLength);
             }, errorCallback);
-        }, errorCallback, [null, fileName]);
+        }, errorCallback, [fileName, null]);
     };
 
     exports.readAsText = function(successCallback, errorCallback, args) {
@@ -275,7 +303,7 @@ QUIRKS:
         // Create an absolute path if we were handed a relative one.
         path = resolveToFullPath_(fullPath, path);
 
-        idb_.get(path, function(folderEntry) {
+        idb_.get(path.storagePath, function(folderEntry) {
             if (!options) {
                 options = {};
             }
@@ -290,10 +318,9 @@ QUIRKS:
                 // If create is true, the path doesn't exist, and no other 
error occurs,
                 // getDirectory must create it as a zero-length file and 
return a corresponding
                 // MyDirectoryEntry.
-                var name = path.split(DIR_SEPARATOR).pop(); // Just need 
filename.
-                var dirEntry = new DirectoryEntry(name, path, fs_);
+                var dirEntry = new DirectoryEntry(path.fileName, 
path.fullPath, new FileSystem(path.fsName, fs_.root));
 
-                idb_.put(dirEntry, successCallback, errorCallback);
+                idb_.put(dirEntry, path.storagePath, successCallback, 
errorCallback);
             } else if (options.create === true && folderEntry) {
 
                 if (folderEntry.isDirectory) {
@@ -306,9 +333,8 @@ QUIRKS:
                 }
             } else if ((!options.create || options.create === false) && 
!folderEntry) {
                 // Handle root special. It should always exist.
-                if (path == DIR_SEPARATOR) {
-                    folderEntry = new DirectoryEntry('', DIR_SEPARATOR, fs_);
-                    successCallback(folderEntry);
+                if (path.fullPath === DIR_SEPARATOR) {
+                    successCallback(fs_.root);
                     return;
                 }
 
@@ -362,11 +388,11 @@ QUIRKS:
 
                 exports.write(function() {
                     successCallback(dstFileEntry);
-                }, errorCallback, [dstFileEntry.fullPath, 
srcFileEntry.file_.blob_, 0]);
+                }, errorCallback, [dstFileEntry.file_.storagePath, 
srcFileEntry.file_.blob_, 0]);
 
             }, errorCallback, [parentFullPath, name, {create: true}]);
 
-        }, errorCallback, [null, srcPath]);
+        }, errorCallback, [srcPath, null]);
     };
 
     exports.moveTo = function(successCallback, errorCallback, args) {
@@ -383,6 +409,78 @@ QUIRKS:
         }, errorCallback, args);
     };
 
+    exports.resolveLocalFileSystemURI = function(successCallback, 
errorCallback, args) {
+        var path = args[0];
+
+        // Ignore parameters
+        if (path.indexOf('?') !== -1) {
+            path = String(path).split("?")[0];
+        }
+
+        // support for encodeURI
+        if (/\%5/g.test(path)) {
+            path = decodeURI(path);
+        }
+
+        if (path.indexOf(pathsPrefix.dataDirectory) === 0) {
+            path = path.substring(pathsPrefix.dataDirectory.length - 1);
+
+            exports.requestFileSystem(function(fs) {
+                fs.root.getFile(path, {create: false}, successCallback, 
function() {
+                    fs.root.getDirectory(path, {create: false}, 
successCallback, errorCallback);
+                });
+            }, errorCallback, [LocalFileSystem.PERSISTENT]);
+        } else if (path.indexOf(pathsPrefix.cacheDirectory) === 0) {
+            path = path.substring(pathsPrefix.cacheDirectory.length - 1);
+
+            exports.requestFileSystem(function(fs) {
+                fs.root.getFile(path, {create: false}, successCallback, 
function() {
+                    fs.root.getDirectory(path, {create: false}, 
successCallback, errorCallback);
+                });
+            }, errorCallback, [LocalFileSystem.TEMPORARY]);
+        } else if (path.indexOf(pathsPrefix.applicationDirectory) === 0) {
+            path = path.substring(pathsPrefix.applicationDirectory.length);
+
+            var xhr = new XMLHttpRequest();
+            xhr.open("GET", path, true);
+            xhr.onreadystatechange = function () {
+                if (xhr.status === 200 && xhr.readyState === 4) {
+                    exports.requestFileSystem(function(fs) {
+                        fs.name = location.hostname;
+                        fs.root.getFile(path, {create: true}, writeFile, 
errorCallback);
+                    }, errorCallback, [LocalFileSystem.PERSISTENT]);
+                }
+            };
+
+            xhr.onerror = function () {
+                errorCallback && errorCallback(FileError.NOT_READABLE_ERR);
+            };
+
+            xhr.send();
+        } else {
+            errorCallback && errorCallback(FileError.NOT_FOUND_ERR);
+        }
+
+        function writeFile(entry) {
+            entry.createWriter(function (fileWriter) {
+                fileWriter.onwriteend = function (evt) {
+                    if (!evt.target.error) {
+                        entry.filesystemName = location.hostname;
+                        successCallback(entry);
+                    }
+                };
+                fileWriter.onerror = function () {
+                    errorCallback && errorCallback(FileError.NOT_READABLE_ERR);
+                };
+                fileWriter.write(new Blob([xhr.response]));
+            }, errorCallback);
+        }
+    };
+
+    exports.requestAllPaths = function(successCallback) {
+        successCallback(pathsPrefix);
+    };
+
 /*** Helpers ***/
 
     /**
@@ -397,12 +495,13 @@ QUIRKS:
      * @constructor
      */
     function MyFile(opts) {
-        var blob_ = null;
+        var blob_ = new Blob();
 
         this.size = opts.size || 0;
         this.name = opts.name || '';
         this.type = opts.type || '';
         this.lastModifiedDate = opts.lastModifiedDate || null;
+        this.storagePath = opts.storagePath || '';
 
         // Need some black magic to correct the object's size/name/type based 
on the
         // blob that is saved.
@@ -427,9 +526,17 @@ QUIRKS:
     // end with one (e.g. a directory). Also, resolve '.' and '..' to an 
absolute
     // one. This method ensures path is legit!
     function resolveToFullPath_(cwdFullPath, path) {
+        path = path || '';
         var fullPath = path;
+        var prefix = '';
 
-        var relativePath = path[0] != DIR_SEPARATOR;
+        cwdFullPath = cwdFullPath || DIR_SEPARATOR;
+        if (cwdFullPath.indexOf(FILESYSTEM_PREFIX) === 0) {
+            prefix = cwdFullPath.substring(0, 
cwdFullPath.indexOf(DIR_SEPARATOR, FILESYSTEM_PREFIX.length));
+            cwdFullPath = 
cwdFullPath.substring(cwdFullPath.indexOf(DIR_SEPARATOR, 
FILESYSTEM_PREFIX.length));
+        }
+
+        var relativePath = path[0] !== DIR_SEPARATOR;
         if (relativePath) {
             fullPath = cwdFullPath;
             if (cwdFullPath != DIR_SEPARATOR) {
@@ -453,7 +560,7 @@ QUIRKS:
         }).join(DIR_SEPARATOR);
 
         // Add back in leading slash.
-        if (fullPath[0] != DIR_SEPARATOR) {
+        if (fullPath[0] !== DIR_SEPARATOR) {
             fullPath = DIR_SEPARATOR + fullPath;
         }
 
@@ -472,7 +579,12 @@ QUIRKS:
             fullPath = fullPath.substring(0, fullPath.length - 1);
         }
 
-        return fullPath;
+        return {
+            storagePath: prefix + fullPath,
+            fullPath: fullPath,
+            fileName: fullPath.split(DIR_SEPARATOR).pop(),
+            fsName: prefix.split(DIR_SEPARATOR).pop()
+        };
     }
 
     function fileEntryFromIdbEntry(fileEntry) {
@@ -509,7 +621,7 @@ QUIRKS:
                     break;
             }
 
-        }, errorCallback, [null, fullPath]);
+        }, errorCallback, [fullPath, null]);
     }
 
 /*** Core logic to handle IDB operations ***/
@@ -552,6 +664,7 @@ QUIRKS:
 
     idb_.get = function(fullPath, successCallback, errorCallback) {
         if (!this.db) {
+            errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR);
             return;
         }
 
@@ -568,41 +681,35 @@ QUIRKS:
         };
     };
 
-    idb_.getAllEntries = function(fullPath, successCallback, errorCallback) {
+    idb_.getAllEntries = function(fullPath, storagePath, successCallback, 
errorCallback) {
         if (!this.db) {
+            errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR);
             return;
         }
 
         var results = [];
 
-        //var range = IDBKeyRange.lowerBound(fullPath, true);
-        //var range = IDBKeyRange.upperBound(fullPath, true);
-
-        // Treat the root entry special. Querying it returns all entries 
because
-        // they match '/'.
-        var range = null;
-        if (fullPath != DIR_SEPARATOR) {
-            //console.log(fullPath + '/', fullPath + DIR_OPEN_BOUND)
-            range = IDBKeyRange.bound(
-                    fullPath + DIR_SEPARATOR, fullPath + DIR_OPEN_BOUND, 
false, true);
+        if (storagePath[storagePath.length - 1] === DIR_SEPARATOR) {
+            storagePath = storagePath.substring(0, storagePath.length - 1);
         }
 
+        range = IDBKeyRange.bound(
+                storagePath + DIR_SEPARATOR, storagePath + DIR_OPEN_BOUND, 
false, true);
+
         var tx = this.db.transaction([FILE_STORE_], 'readonly');
         tx.onabort = errorCallback || onError;
         tx.oncomplete = function(e) {
-            // TODO: figure out how to do be range queries instead of 
filtering result
-            // in memory :(
             results = results.filter(function(val) {
                 var valPartsLen = val.fullPath.split(DIR_SEPARATOR).length;
                 var fullPathPartsLen = fullPath.split(DIR_SEPARATOR).length;
 
-                if (fullPath == DIR_SEPARATOR && valPartsLen < 
fullPathPartsLen + 1) {
+                if (fullPath === DIR_SEPARATOR && valPartsLen < 
fullPathPartsLen + 1) {
                     // Hack to filter out entries in the root folder. This is 
inefficient
                     // because reading the entires of fs.root (e.g. '/') 
returns ALL
                     // results in the database, then filters out the entries 
not in '/'.
                     return val;
-                } else if (fullPath != DIR_SEPARATOR &&
-                    valPartsLen == fullPathPartsLen + 1) {
+                } else if (fullPath !== DIR_SEPARATOR &&
+                    valPartsLen === fullPathPartsLen + 1) {
                     // If this a subfolder and entry is a direct child, 
include it in
                     // the results. Otherwise, it's not an entry of this 
folder.
                     return val;
@@ -627,6 +734,7 @@ QUIRKS:
 
     idb_['delete'] = function(fullPath, successCallback, errorCallback) {
         if (!this.db) {
+            errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR);
             return;
         }
 
@@ -637,11 +745,12 @@ QUIRKS:
         //var request = tx.objectStore(FILE_STORE_).delete(fullPath);
         var range = IDBKeyRange.bound(
             fullPath, fullPath + DIR_OPEN_BOUND, false, true);
-        var request = tx.objectStore(FILE_STORE_)['delete'](range);
+        tx.objectStore(FILE_STORE_)['delete'](range);
     };
 
-    idb_.put = function(entry, successCallback, errorCallback) {
+    idb_.put = function(entry, storagePath, successCallback, errorCallback) {
         if (!this.db) {
+            errorCallback && errorCallback(FileError.INVALID_MODIFICATION_ERR);
             return;
         }
 
@@ -652,7 +761,7 @@ QUIRKS:
             successCallback(entry);
         };
 
-        var request = tx.objectStore(FILE_STORE_).put(entry, entry.fullPath);
+        tx.objectStore(FILE_STORE_).put(entry, storagePath);
     };
 
     // Global error handler. Errors bubble from request, to transaction, to db.

http://git-wip-us.apache.org/repos/asf/cordova-plugin-file/blob/98fed5f7/www/firefoxos/FileSystem.js
----------------------------------------------------------------------
diff --git a/www/firefoxos/FileSystem.js b/www/firefoxos/FileSystem.js
new file mode 100644
index 0000000..23daa07
--- /dev/null
+++ b/www/firefoxos/FileSystem.js
@@ -0,0 +1,29 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+FILESYSTEM_PREFIX = "cdvfile://";
+
+module.exports = {
+    __format__: function(fullPath) {
+        return (FILESYSTEM_PREFIX + this.name + (fullPath[0] === '/' ? '' : 
'/') + encodeURI(fullPath));
+    }
+};
+

Reply via email to