From 3d2428c19f46d3c75c52f6e942c9d88f9c0bcb99 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Wed, 14 Dec 2011 15:05:25 -0800 Subject: [PATCH 01/13] Initial commit for FileSaver --- src/NativeFileSystem.js | 94 +++++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 350cc8ffca8..6775cf4bf36 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -136,6 +136,71 @@ FileEntry = function( name ) { // IMPLEMENT LATER void file (FileCallback successCallback, optional ErrorCallback errorCallback); }; +/** createWriter + * + Creates a new FileWriter associated with the file that this FileEntry represents. + * + * @param {function} successCallback + * @param {function} errorCallback + */ +FileEntry.prototype.createWriter = function( successCallback, errorCallback ) { + var fileWriter = new FileWriter(); + successCallback( fileWriter ); +}; + +/** class: FileSaver + * + * @param {Blob} data + * @constructor + */ +FileSaver = function( data ) { + _data = data; +}; + +// FileSaver constants +Object.defineProperties(FileSaver, + { INIT: { value: 1 } + , WRITING: { value: 2 } + , DONE: { value: 3 } +}); + +// FileSaver private memeber vars +FileSaver.prototype._data = null; +FileSaver.prototype._readyState = FileSaver.INIT; +FileSaver.prototype._error = null; + +// FileSaver methods + +// TODO (jasonsj): http://dev.w3.org/2009/dap/file-system/file-writer.html#widl-FileSaver-abort-void +FileSaver.prototype.abort = function() { + // If readyState is DONE or INIT, terminate this overall series of steps without doing anything else.. + if (_readyState == FileSaver.INIT || _readyState == FileSaver.DONE) + return; + + // Terminate any steps having to do with writing a file. + + // Set the error attribute to a FileError object with the code ABORT_ERR. + _error = new FileError(FileError.ABORT_ERR); + + // Set readyState to DONE. + _readyState = FileSaver.DONE; + + // Dispatch a progress event called abort + // Dispatch a progress event called writeend + // Stop dispatching any further progress events. + // Terminate this overall set of steps. + + return err; +}; + +/** class: FileWriter + * + * @constructor + * @extends {FileSaver} + */ +FileWriter = function( ) { + FileSaver.call(this); +}; /** class: DirectoryEntry * @@ -223,17 +288,18 @@ FileError = function(code) { this.code = code || 0; }; -$.extend(FileError, { - NOT_FOUND_ERR: 1, - SECURITY_ERR: 2, - ABORT_ERR: 3, - NOT_READABLE_ERR: 4, - ENCODING_ERR: 5, - NO_MODIFICATION_ALLOWED_ERR: 6, - INVALID_STATE_ERR: 7, - SYNTAX_ERR: 8, - INVALID_MODIFICATION_ERR: 9, - QUOTA_EXCEEDED_ERR: 10, - TYPE_MISMATCH_ERR: 11, - PATH_EXISTS_ERR: 12 -}); +// FileSaver constants +Object.defineProperties(FileError, + { NOT_FOUND_ERR: { value: 1 } + , SECURITY_ERR: { value: 2 } + , ABORT_ERR: { value: 3 } + , NOT_READABLE_ERR: { value: 4 } + , ENCODING_ERR: { value: 5 } + , NO_MODIFICATION_ALLOWED_ERR: { value: 6 } + , INVALID_STATE_ERR: { value: 7 } + , SYNTAX_ERR: { value: 8 } + , INVALID_MODIFICATION_ERR: { value: 9 } + , QUOTA_EXCEEDED_ERR: { value: 10 } + , TYPE_MISMATCH_ERR: { value: 11 } + , PATH_EXISTS_ERR: { value: 12 } +}); \ No newline at end of file From 0bc6854366a54004a9fe15de3c9405d3eafeae96 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Wed, 14 Dec 2011 16:00:50 -0800 Subject: [PATCH 02/13] Initial commit for FileWriter --- src/NativeFileSystem.js | 106 +++++++++------- test/spec/NativeFileSystem-test.js | 193 ++++++++++++++++------------- 2 files changed, 169 insertions(+), 130 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 6775cf4bf36..dd33254cb67 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -10,9 +10,9 @@ window.NativeFileSystem = { * @param {string[]} fileTypes * @param {function} resultCallback * @constructor - */ + */ showOpenDialog: function ( allowMultipleSelection, - chooseDirectories, + chooseDirectories, title, initialPath, fileTypes, @@ -20,9 +20,9 @@ window.NativeFileSystem = { errorCallback ) { if( !successCallback ) return; - + var files = brackets.fs.showOpenDialog( allowMultipleSelection, - chooseDirectories, + chooseDirectories, title, initialPath, fileTypes, @@ -31,8 +31,8 @@ window.NativeFileSystem = { successCallback( data ); else if (errorCallback) errorCallback(NativeFileSystem._nativeToFileError(err)); - }); - + }); + }, /** requestNativeFileSystem @@ -50,14 +50,14 @@ window.NativeFileSystem = { else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); } - }); + }); }, - + _nativeToFileError: function(nativeErr) { // The HTML file spec says SECURITY_ERR is a catch-all to be used in situations // not covered by other error codes. var error = FileError.SECURITY_ERR; - + switch (nativeErr) { // We map ERR_UNKNOWN and ERR_INVALID_PARAMS to SECURITY_ERR, // since there aren't specific mappings for these. @@ -65,20 +65,20 @@ window.NativeFileSystem = { case brackets.fs.ERR_INVALID_PARAMS: error = FileError.SECURITY_ERR; break; - + case brackets.fs.ERR_NOT_FOUND: error = FileError.NOT_FOUND_ERR; break; case brackets.fs.ERR_CANT_READ: error = FileError.NOT_READABLE_ERR; break; - + // It might seem like you should use FileError.ENCODING_ERR for this, - // but according to the spec that's for malformed URLs. + // but according to the spec that's for malformed URLs. case brackets.fs.ERR_UNSUPPORTED_ENCODING: error = FileError.SECURITY_ERR; break; - + case brackets.fs.ERR_CANT_WRITE: error = FileError.NO_MODIFICATION_ALLOWED_ERR; break; @@ -86,7 +86,7 @@ window.NativeFileSystem = { error = FileError.QUOTA_EXCEEDED_ERR; break; } - + return new FileError(error); } }; @@ -102,7 +102,7 @@ Entry = function( fullPath, isDirectory) { this.isFile = !isDirectory; // IMPLEMENT LATER void getMetadata (MetadataCallback successCallback, optional ErrorCallback errorCallback); this.fullPath = fullPath; - + // Extract name from fullPath this.name = null; // default if extraction fails if( fullPath ){ @@ -110,7 +110,7 @@ Entry = function( fullPath, isDirectory) { if( pathParts.length > 0 ) this.name = pathParts.pop(); } - + // IMPLEMENT LATER var filesystem; // IMPLEMENT LATER void moveTo (DirectoryEntry parent, optional DOMString newName, optional EntryCallback successCallback, optional ErrorCallback errorCallback); // IMPLEMENT LATER void copyTo (DirectoryEntry parent, optional DOMString newName, optional EntryCallback successCallback, optional ErrorCallback errorCallback); @@ -126,19 +126,18 @@ Entry = function( fullPath, isDirectory) { * @param {string} name * @constructor * @extends {Entry} - */ + */ FileEntry = function( name ) { Entry.call(this, name, false); - + // TODO: make FileEntry actually inherit from Entry by modifying prototype. I don't know how to do this yet. - - // IMPLEMENT LATER void createWriter (FileWriterCallback successCallback, optional ErrorCallback errorCallback); + // IMPLEMENT LATER void file (FileCallback successCallback, optional ErrorCallback errorCallback); }; /** createWriter - * - Creates a new FileWriter associated with the file that this FileEntry represents. + * + Creates a new FileWriter associated with the file that this FileEntry represents. * * @param {function} successCallback * @param {function} errorCallback @@ -152,13 +151,13 @@ FileEntry.prototype.createWriter = function( successCallback, errorCallback ) { * * @param {Blob} data * @constructor - */ + */ FileSaver = function( data ) { _data = data; }; // FileSaver constants -Object.defineProperties(FileSaver, +Object.defineProperties(FileSaver, { INIT: { value: 1 } , WRITING: { value: 2 } , DONE: { value: 3 } @@ -176,12 +175,12 @@ FileSaver.prototype.abort = function() { // If readyState is DONE or INIT, terminate this overall series of steps without doing anything else.. if (_readyState == FileSaver.INIT || _readyState == FileSaver.DONE) return; - + // Terminate any steps having to do with writing a file. // Set the error attribute to a FileError object with the code ABORT_ERR. _error = new FileError(FileError.ABORT_ERR); - + // Set readyState to DONE. _readyState = FileSaver.DONE; @@ -189,28 +188,47 @@ FileSaver.prototype.abort = function() { // Dispatch a progress event called writeend // Stop dispatching any further progress events. // Terminate this overall set of steps. - - return err; + + return err; }; /** class: FileWriter * * @constructor * @extends {FileSaver} - */ + */ FileWriter = function( ) { FileSaver.call(this); }; +// FileWriter private memeber vars +FileWriter.prototype._length = 0; +FileWriter.prototype._position = 0; + +Object.defineProperties(FileWriter, + { length: { value: function() { return this._length; }} + , position: { value: function() { return this._position; }} + } +); + +FileWriter.prototype.write = function( data ) { +}; + +FileWriter.prototype.seek = function( offset ) { +}; + +FileWriter.prototype.truncate = function( size ) { +}; + /** class: DirectoryEntry * * @param {string} name * @constructor * @extends {Entry} - */ + */ DirectoryEntry = function( name ) { Entry.call(this, name, true); - + // TODO: make DirectoryEntry actually inherit from Entry by modifying prototype. I don't know how to do this yet. // IMPLEMENT LATERvoid getFile (DOMString path, optional Flags options, optional EntryCallback successCallback, optional ErrorCallback errorCallback); @@ -222,15 +240,15 @@ DirectoryEntry = function( name ) { DirectoryEntry.prototype.createReader = function() { var dirReader = new DirectoryReader(); dirReader._directory = this; - + return dirReader; }; /** class: DirectoryReader - */ + */ DirectoryReader = function() { - + }; @@ -239,7 +257,7 @@ DirectoryReader = function() { * @param {function} successCallback * @param {function} errorCallback * @returns {Entry[]} - */ + */ DirectoryReader.prototype.readEntries = function( successCallback, errorCallback ){ var rootPath = this._directory.fullPath; var jsonList = brackets.fs.readdir( rootPath, function( err, filelist ) { @@ -248,33 +266,33 @@ DirectoryReader.prototype.readEntries = function( successCallback, errorCallback var entries = []; filelist.forEach(function(item){ var itemFullPath = rootPath + "/" + item; - + brackets.fs.stat( itemFullPath, function( err, statData) { - + if( !err ){ if( statData.isDirectory( itemFullPath ) ) entries.push( new DirectoryEntry( itemFullPath ) ); - else if( statData.isFile( itemFullPath ) ) + else if( statData.isFile( itemFullPath ) ) entries.push( new FileEntry( itemFullPath ) ); } else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); } - - }) + + }) }); - successCallback( entries ); + successCallback( entries ); } else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); } - }); + }); }; /** class: FileError * - * Implementation of HTML file API error code return class. Note that the + * Implementation of HTML file API error code return class. Note that the * various HTML file API specs are not consistent in their definition of * some error code values like ABORT_ERR; I'm using the definitions from * the Directories and System spec since it seems to be the most @@ -289,7 +307,7 @@ FileError = function(code) { }; // FileSaver constants -Object.defineProperties(FileError, +Object.defineProperties(FileError, { NOT_FOUND_ERR: { value: 1 } , SECURITY_ERR: { value: 2 } , ABORT_ERR: { value: 3 } diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 0b8a045bbb7..9b99575ac8d 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -1,101 +1,122 @@ -describe("NativeFileSystem", function(){ +describe("NativeFileSystem", function() { - describe("Reading", function() { + beforeEach(function () { + this.path = SpecRunnerUtils.getTestPath("/spec/NativeFileSystem-test-files"); + }); - beforeEach(function() { - this.addMatchers({ - toContainDirectoryWithName: function(expected) { - for (var i = 0 ; i < this.actual.length; ++i) { - if (this.actual[i].isDirectory && this.actual[i].name === expected) { - return true; - } - } - return false; - } - , toContainFileWithName: function(expected) { - for (var i = 0 ; i < this.actual.length; ++i) { - if (this.actual[i].isFile && this.actual[i].name === expected) { - return true; + describe("Reading", function() { + + beforeEach(function() { + this.addMatchers({ + toContainDirectoryWithName: function(expected) { + for (var i = 0 ; i < this.actual.length; ++i) { + if (this.actual[i].isDirectory && this.actual[i].name === expected) { + return true; + } + } + return false; + } + , toContainFileWithName: function(expected) { + for (var i = 0 ; i < this.actual.length; ++i) { + if (this.actual[i].isFile && this.actual[i].name === expected) { + return true; + } + } + return false; + } + }); + }); + + it("should read a directory from disk", function() { + var entries = null; + var readComplete = false; + + var nfs = window.NativeFileSystem.requestNativeFileSystem(this.path, requestNativeFileSystemSuccessCB); + function requestNativeFileSystemSuccessCB( nfs ){ + var reader = nfs.createReader(); + + var successCallback = function(e) { entries = e; readComplete = true; } + // TODO: not sure what parameters error callback will take because it's not implemented yet + var errorCallback = function() { readComplete = true; } + + reader.readEntries(successCallback, errorCallback); + + waitsFor(function() { return readComplete; }, 1000); + + runs(function() { + expect(entries).toContainDirectoryWithName("dir1"); + expect(entries).toContainFileWithName("file1"); + expect(entries).not.toContainFileWithName("file2"); + }); } - } - return false; - } - }); + }); - this.path = SpecRunnerUtils.getTestPath("/spec/NativeFileSystem-test-files"); - }); + it("should return an error if the directory doesn't exist", function() { + var successCalled = false, errorCalled = false, error = null; + window.NativeFileSystem.requestNativeFileSystem(this.path + '/nonexistent-dir', function(data) { + successCalled = true; + }, function(err) { + errorCalled = true; + error = err; + }); + + waitsFor(function() { return successCalled || errorCalled; }, 1000); - it("should read a directory from disk", function() { - var entries = null; - var readComplete = false; - - var nfs = window.NativeFileSystem.requestNativeFileSystem(this.path, requestNativeFileSystemSuccessCB); - function requestNativeFileSystemSuccessCB( nfs ){ - var reader = nfs.createReader(); - - var successCallback = function(e) { entries = e; readComplete = true; } - // TODO: not sure what parameters error callback will take because it's not implemented yet - var errorCallback = function() { readComplete = true; } - - reader.readEntries(successCallback, errorCallback); - - waitsFor(function() { return readComplete; }, 1000); - runs(function() { - expect(entries).toContainDirectoryWithName("dir1"); - expect(entries).toContainFileWithName("file1"); - expect(entries).not.toContainFileWithName("file2"); + expect(successCalled).toBe(false); + expect(errorCalled).toBe(true); + expect(error.code).toBe(FileError.NOT_FOUND_ERR); + }); + }); + + it("should return an error if you pass a bad parameter", function() { + var successCalled = false, errorCalled = false, error = null; + window.NativeFileSystem.requestNativeFileSystem(0xDEADBEEF, function(data) { + successCalled = true; + }, function(err) { + errorCalled = true; + error = err; + }); + + waitsFor(function() { return successCalled || errorCalled; }, 1000); + + runs(function() { + expect(successCalled).toBe(false); + expect(errorCalled).toBe(true); + expect(error.code).toBe(FileError.SECURITY_ERR); }); - } - }); - - it("should return an error if the directory doesn't exist", function() { - var successCalled = false, errorCalled = false, error = null; - window.NativeFileSystem.requestNativeFileSystem(this.path + '/nonexistent-dir', function(data) { - successCalled = true; - }, function(err) { - errorCalled = true; - error = err; }); - - waitsFor(function() { return successCalled || errorCalled; }, 1000); - - runs(function() { - expect(successCalled).toBe(false); - expect(errorCalled).toBe(true); - expect(error.code).toBe(FileError.NOT_FOUND_ERR); + + it("should be okay to not pass an error callback", function() { + var entries = null; + window.NativeFileSystem.requestNativeFileSystem(this.path, function(data) { + entries = data; + }); + + waitsFor(function() { return entries != null; }, 1000); + + runs(function() { + expect(entries).not.toBe(null); + }); }); }); - - it("should return an error if you pass a bad parameter", function() { - var successCalled = false, errorCalled = false, error = null; - window.NativeFileSystem.requestNativeFileSystem(0xDEADBEEF, function(data) { - successCalled = true; - }, function(err) { - errorCalled = true; - error = err; + + describe("Writing", function() { + + it("should write new files", function() { + expect(1); }); - - waitsFor(function() { return successCalled || errorCalled; }, 1000); - - runs(function() { - expect(successCalled).toBe(false); - expect(errorCalled).toBe(true); - expect(error.code).toBe(FileError.SECURITY_ERR); - }); - }); - - it("should be okay to not pass an error callback", function() { - var entries = null; - window.NativeFileSystem.requestNativeFileSystem(this.path, function(data) { - entries = data; + + it("should append to existing files", function() { + expect(1); + }); + + it("should seek into a file before writing", function() { + expect(1); }); - - waitsFor(function() { return entries != null; }, 1000); - - runs(function() { - expect(entries).not.toBe(null); + + it("should truncate files", function() { + expect(1); }); }); - }); }); From 7d60cdc25237b9cd2e1532faeb210353d4a9cacf Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 15 Dec 2011 14:04:15 -0800 Subject: [PATCH 03/13] DirectoryEntry.getFile() impl --- src/NativeFileSystem.js | 101 ++++++++++++++++++++--------- test/spec/NativeFileSystem-test.js | 33 ++++++++-- 2 files changed, 100 insertions(+), 34 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index dd33254cb67..d31685868cd 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -143,7 +143,41 @@ FileEntry = function( name ) { * @param {function} errorCallback */ FileEntry.prototype.createWriter = function( successCallback, errorCallback ) { - var fileWriter = new FileWriter(); + _FileWriter = function( data ) { + FileSaver.call(this, data); + }; + + // FileWriter private memeber vars + _FileWriter.prototype._length = 0; + _FileWriter.prototype._position = 0; + + _FileWriter.prototype.length = function( ) { + return this._length; + }; + + _FileWriter.prototype.position = function( ) { + return this._position; + }; + + _FileWriter.prototype.write = function( data ) { + brackets.fs.writeFile(baseDir + "write_test.txt", contents, "utf8", function(err) { + expect(err).toBeFalsy(); + + // Read contents to verify + brackets.fs.readFile(baseDir + "write_test.txt", "utf8", function(err, data) { + expect(err).toBeFalsy(); + expect(data).toBe(contents); + }); + }); + }; + + _FileWriter.prototype.seek = function( offset ) { + }; + + _FileWriter.prototype.truncate = function( size ) { + }; + + var fileWriter = new _FileWriter(); successCallback( fileWriter ); }; @@ -192,34 +226,6 @@ FileSaver.prototype.abort = function() { return err; }; -/** class: FileWriter - * - * @constructor - * @extends {FileSaver} - */ -FileWriter = function( ) { - FileSaver.call(this); -}; - -// FileWriter private memeber vars -FileWriter.prototype._length = 0; -FileWriter.prototype._position = 0; - -Object.defineProperties(FileWriter, - { length: { value: function() { return this._length; }} - , position: { value: function() { return this._position; }} - } -); - -FileWriter.prototype.write = function( data ) { -}; - -FileWriter.prototype.seek = function( offset ) { -}; - -FileWriter.prototype.truncate = function( size ) { -}; - /** class: DirectoryEntry * * @param {string} name @@ -231,7 +237,6 @@ DirectoryEntry = function( name ) { // TODO: make DirectoryEntry actually inherit from Entry by modifying prototype. I don't know how to do this yet. - // IMPLEMENT LATERvoid getFile (DOMString path, optional Flags options, optional EntryCallback successCallback, optional ErrorCallback errorCallback); // IMPLEMENT LATERvoid getDirectory (DOMString path, optional Flags options, optional EntryCallback successCallback, optional ErrorCallback errorCallback); // IMPLEMENT LATERvoid removeRecursively (VoidCallback successCallback, optional ErrorCallback errorCallback); }; @@ -244,6 +249,42 @@ DirectoryEntry.prototype.createReader = function() { return dirReader; }; +DirectoryEntry.prototype.getFile = function( path, options, successCallback, errorCallback ) { + // TODO (jasonsj): handle absolute paths + var fileFullPath = this.fullPath + "/" + path; + + // Use stat() to check if file exists + brackets.fs.stat( fileFullPath, function( err, stats ) { + if ( options.create ) { + if ( ( options.exclusive && ( err !== FileError.ERR_NOT_FOUND ) ) { + // throw error if file already exists + errorCallback( new FileError( FileError.PATH_EXISTS_ERR ) ); + return; + } + if ( err === FileError.ERR_NOT_FOUND ) { + brackets.fs.writeFile( fileFullPath, "utf8", "", function( err ) { + if ( err ) + errorCallback( new FileError( err ) ); + else + successCallback( new FileEntry( fileFullPath ) ); + }); + + return; + } + } + else { + // file does not exist + if ( err === FileError.ERR_NOT_FOUND ) + errorCallback( new FileError( FileError.ERR_NOT_FOUND ) ); + // path is a directory and not a file + else if ( stats.isDirectory ) + errorCallback( new FileError( FileError.TYPE_MISMATCH_ERR ) ); + else + successCallback( new FileEntry( fileFullPath ) ); + } + }); +}; + /** class: DirectoryReader */ diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 9b99575ac8d..0b2146c0f46 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -104,19 +104,44 @@ describe("NativeFileSystem", function() { describe("Writing", function() { it("should write new files", function() { - expect(1); + var nfs = null; + + window.NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { + nfs = fs; + }); + + waitsFor( function() { return nfs }, 1000); + + var fileEntry = null; + var writeComplete = false; + + runs(function() { + var successCallback = function( entry ) { + fileEntry = entry; + writeComplete = true; + } + var errorCallback = function() { writeComplete = true }; + + nfs.root.getFile("should-write-new-files.txt", { create: true }, successCallback, errorCallback ); + }); + + waitsFor( function() { return writeComplete; }, 1000 ); + + runs(function() { + expect(entries).not.toBe(null); + }); }); it("should append to existing files", function() { - expect(1); + this.fail("TODO"); }); it("should seek into a file before writing", function() { - expect(1); + this.fail("TODO"); }); it("should truncate files", function() { - expect(1); + this.fail("TODO"); }); }); }); From 38fa49286cc59f3b903c4987fa0a659f4b6f0582 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 15 Dec 2011 14:26:11 -0800 Subject: [PATCH 04/13] Resolve merge conflicts --- src/NativeFileSystem.js | 270 +++++++++++++++++++++++++++++----------- 1 file changed, 199 insertions(+), 71 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 4e2b81af9a9..9f41baf66ea 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -14,9 +14,9 @@ var NativeFileSystem = { * @param {string[]} fileTypes * @param {function} resultCallback * @constructor - */ + */ showOpenDialog: function ( allowMultipleSelection, - chooseDirectories, + chooseDirectories, title, initialPath, fileTypes, @@ -24,9 +24,9 @@ var NativeFileSystem = { errorCallback ) { if( !successCallback ) return; - + var files = brackets.fs.showOpenDialog( allowMultipleSelection, - chooseDirectories, + chooseDirectories, title, initialPath, fileTypes, @@ -35,8 +35,8 @@ var NativeFileSystem = { successCallback( data ); else if (errorCallback) errorCallback(NativeFileSystem._nativeToFileError(err)); - }); - + }); + }, /** requestNativeFileSystem @@ -54,14 +54,14 @@ var NativeFileSystem = { else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); } - }); + }); }, - + _nativeToFileError: function(nativeErr) { // The HTML file spec says SECURITY_ERR is a catch-all to be used in situations // not covered by other error codes. var error = FileError.SECURITY_ERR; - + switch (nativeErr) { // We map ERR_UNKNOWN and ERR_INVALID_PARAMS to SECURITY_ERR, // since there aren't specific mappings for these. @@ -69,20 +69,20 @@ var NativeFileSystem = { case NativeFileSystem.ERR_INVALID_PARAMS: error = FileError.SECURITY_ERR; break; - + case NativeFileSystem.ERR_NOT_FOUND: error = FileError.NOT_FOUND_ERR; break; case NativeFileSystem.ERR_CANT_READ: error = FileError.NOT_READABLE_ERR; break; - + // It might seem like you should use FileError.ENCODING_ERR for this, - // but according to the spec that's for malformed URLs. + // but according to the spec that's for malformed URLs. case NativeFileSystem.ERR_UNSUPPORTED_ENCODING: error = FileError.SECURITY_ERR; break; - + case NativeFileSystem.ERR_CANT_WRITE: error = FileError.NO_MODIFICATION_ALLOWED_ERR; break; @@ -90,7 +90,7 @@ var NativeFileSystem = { error = FileError.QUOTA_EXCEEDED_ERR; break; } - + return new FileError(error); } }; @@ -106,7 +106,7 @@ NativeFileSystem.Entry = function( fullPath, isDirectory) { this.isFile = !isDirectory; // IMPLEMENT LATER void getMetadata (MetadataCallback successCallback, optional ErrorCallback errorCallback); this.fullPath = fullPath; - + // Extract name from fullPath this.name = null; // default if extraction fails if( fullPath ){ @@ -114,7 +114,7 @@ NativeFileSystem.Entry = function( fullPath, isDirectory) { if( pathParts.length > 0 ) this.name = pathParts.pop(); } - + // IMPLEMENT LATER var filesystem; // IMPLEMENT LATER void moveTo (DirectoryEntry parent, optional DOMString newName, optional EntryCallback successCallback, optional ErrorCallback errorCallback); // IMPLEMENT LATER void copyTo (DirectoryEntry parent, optional DOMString newName, optional EntryCallback successCallback, optional ErrorCallback errorCallback); @@ -130,12 +130,103 @@ NativeFileSystem.Entry = function( fullPath, isDirectory) { * @param {string} name * @constructor * @extends {Entry} - */ + */ NativeFileSystem.FileEntry = function( name ) { NativeFileSystem.Entry.call(this, name, false); - + // TODO: make FileEntry actually inherit from Entry by modifying prototype. I don't know how to do this yet. - + +}; + +/** createWriter + * + Creates a new FileWriter associated with the file that this FileEntry represents. + * + * @param {function} successCallback + * @param {function} errorCallback + */ +NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, errorCallback ) { + _FileWriter = function( data ) { + FileSaver.call(this, data); + }; + + // FileWriter private memeber vars + _FileWriter.prototype._length = 0; + _FileWriter.prototype._position = 0; + + _FileWriter.prototype.length = function( ) { + return this._length; + }; + + _FileWriter.prototype.position = function( ) { + return this._position; + }; + + _FileWriter.prototype.write = function( data ) { + brackets.fs.writeFile(baseDir + "write_test.txt", contents, "utf8", function(err) { + expect(err).toBeFalsy(); + + // Read contents to verify + brackets.fs.readFile(baseDir + "write_test.txt", "utf8", function(err, data) { + expect(err).toBeFalsy(); + expect(data).toBe(contents); + }); + }); + }; + + _FileWriter.prototype.seek = function( offset ) { + }; + + _FileWriter.prototype.truncate = function( size ) { + }; + + var fileWriter = new _FileWriter(); + successCallback( fileWriter ); +}; + +/** class: FileSaver + * + * @param {Blob} data + * @constructor + */ +FileSaver = function( data ) { + _data = data; +}; + +// FileSaver constants +Object.defineProperties(FileSaver, + { INIT: { value: 1 } + , WRITING: { value: 2 } + , DONE: { value: 3 } +}); + +// FileSaver private memeber vars +NativeFileSystem.FileSaver.prototype._data = null; +NativeFileSystem.FileSaver.prototype._readyState = FileSaver.INIT; +NativeFileSystem.FileSaver.prototype._error = null; + +// FileSaver methods + +// TODO (jasonsj): http://dev.w3.org/2009/dap/file-system/file-writer.html#widl-FileSaver-abort-void +NativeFileSystem.FileSaver.prototype.abort = function() { + // If readyState is DONE or INIT, terminate this overall series of steps without doing anything else.. + if (_readyState == FileSaver.INIT || _readyState == FileSaver.DONE) + return; + + // Terminate any steps having to do with writing a file. + + // Set the error attribute to a FileError object with the code ABORT_ERR. + _error = new FileError(FileError.ABORT_ERR); + + // Set readyState to DONE. + _readyState = FileSaver.DONE; + + // Dispatch a progress event called abort + // Dispatch a progress event called writeend + // Stop dispatching any further progress events. + // Terminate this overall set of steps. + + return err; }; /** file @@ -146,9 +237,9 @@ NativeFileSystem.FileEntry = function( name ) { * @param {function} errorCallback /* NativeFileSystem.FileEntry.prototype.file = function( successCallback, errorCallback ){ - var newFile = new NativeFileSystem.File( this ); + var newFile = new NativeFileSystem.File( this ); successCallback( newFile ); - + // TODO Ty: error handling // errorCallback }; @@ -164,10 +255,10 @@ NativeFileSystem.FileEntry.prototype.createfileerror = function( successCallback * @constructor * @param {string} name * @extends {Entry} - */ + */ NativeFileSystem.DirectoryEntry = function( name ) { NativeFileSystem.Entry.call(this, name, true); - + // TODO: make DirectoryEntry actually inherit from Entry by modifying prototype. I don't know how to do this yet. // IMPLEMENT LATERvoid getFile (DOMString path, optional Flags options, optional EntryCallback successCallback, optional ErrorCallback errorCallback); @@ -179,15 +270,51 @@ NativeFileSystem.DirectoryEntry = function( name ) { NativeFileSystem.DirectoryEntry.prototype.createReader = function() { var dirReader = new NativeFileSystem.DirectoryReader(); dirReader._directory = this; - + return dirReader; }; +NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, successCallback, errorCallback ) { + // TODO (jasonsj): handle absolute paths + var fileFullPath = this.fullPath + "/" + path; + + // Use stat() to check if file exists + brackets.fs.stat( fileFullPath, function( err, stats ) { + if ( options.create ) { + if ( ( options.exclusive && ( err !== FileError.ERR_NOT_FOUND ) ) { + // throw error if file already exists + errorCallback( new FileError( FileError.PATH_EXISTS_ERR ) ); + return; + } + if ( err === FileError.ERR_NOT_FOUND ) { + brackets.fs.writeFile( fileFullPath, "utf8", "", function( err ) { + if ( err ) + errorCallback( new FileError( err ) ); + else + successCallback( new FileEntry( fileFullPath ) ); + }); + + return; + } + } + else { + // file does not exist + if ( err === FileError.ERR_NOT_FOUND ) + errorCallback( new FileError( FileError.ERR_NOT_FOUND ) ); + // path is a directory and not a file + else if ( stats.isDirectory ) + errorCallback( new FileError( FileError.TYPE_MISMATCH_ERR ) ); + else + successCallback( new FileEntry( fileFullPath ) ); + } + }); +}; + /** class: DirectoryReader - */ + */ NativeFileSystem.DirectoryReader = function() { - + }; @@ -196,7 +323,7 @@ NativeFileSystem.DirectoryReader = function() { * @param {function} successCallback * @param {function} errorCallback * @returns {Entry[]} - */ + */ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallback, errorCallback ){ var rootPath = this._directory.fullPath; var jsonList = brackets.fs.readdir( rootPath, function( err, filelist ) { @@ -205,56 +332,56 @@ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallba var entries = []; filelist.forEach(function(item){ var itemFullPath = rootPath + "/" + item; - + brackets.fs.stat( itemFullPath, function( err, statData) { - + if( !err ){ if( statData.isDirectory( itemFullPath ) ) entries.push( new NativeFileSystem.DirectoryEntry( itemFullPath ) ); - else if( statData.isFile( itemFullPath ) ) + else if( statData.isFile( itemFullPath ) ) entries.push( new NativeFileSystem.FileEntry( itemFullPath ) ); } else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); } - - }) + + }) }); - successCallback( entries ); + successCallback( entries ); } else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); } - }); + }); }; /** class: FileReader * * @extends {EventTarget} - */ + */ NativeFileSystem.FileReader = function() { // Todo Ty: this classes should extend EventTarget - + // async read methods // IMPLEMENT LATER void readAsArrayBuffer(Blob blob); // IMPLEMENT LATER void readAsBinaryString(Blob blob); // IMPLEMENT LATER void readAsDataURL(Blob blob); - + // IMPLEMENT LATER void abort(); - + // states this.EMPTY = 0; this.LOADING = 1; this.DONE = 2; - + // IMPLEMENT LATER readonly attribute unsigned short readyState; - + // File or Blob data // IMPLEMENT LATER readonly attribute any result; // IMPLEMENT LATER readonly attribute DOMError error; - + // event handler attributes this.onloadstart = null; this.onprogress = null; @@ -263,51 +390,51 @@ NativeFileSystem.FileReader = function() { this.onerror = null; this.onloadend = null; - + }; /** readAsText * * @param {Blob} blob * @param {string} encoding - */ + */ NativeFileSystem.FileReader.prototype.readAsText = function( blob, encoding) { var self = this; if( !encoding ) encoding = ""; - + if( this.onloadstart ) this.onloadstart(); // todo params - + brackets.fs.readFile( blob.entry.fullPath, encoding, function( err, data) { - + // TODO Ty // the event objects passed to these event handlers is fake and incomplete right now var fakeEvent = { target: { result: null } }; - + if( err ){ if( self.onerror ) self.onerror(); // TODO Ty: pass event } else{ - + if( self.onprogress ) self.onprogress(); // TODO Ty: pass event - + // note: this.onabort not currently supported - + if( self.onload ){ fakeEvent.target.result = data; self.onload( fakeEvent ); } - + if( self.onloadend ) self.onloadend(); } - + }); }; @@ -315,18 +442,18 @@ NativeFileSystem.FileReader.prototype.readAsText = function( blob, encoding) { * * @constructor * param {Entry} entry - */ + */ NativeFileSystem.Blob = function ( entry ){ this.entry = entry; // IMPLEMENT LATER readonly attribute unsigned long long size; // IMPLEMENT LATER readonly attribute DOMString type; - + //slice Blob into byte-ranged chunks - + // IMPLEMENT LATER Blob slice(optional long long start, // optional long long end, - // optional DOMString contentType); + // optional DOMString contentType); }; /** class: File @@ -334,7 +461,7 @@ NativeFileSystem.Blob = function ( entry ){ * @constructor * param {Entry} entry * @extends {Blob} - */ + */ NativeFileSystem.File = function ( entry ){ NativeFileSystem.Blob.call( this, entry ); @@ -345,7 +472,7 @@ NativeFileSystem.File = function ( entry ){ /** class: FileError * - * Implementation of HTML file API error code return class. Note that the + * Implementation of HTML file API error code return class. Note that the * various HTML file API specs are not consistent in their definition of * some error code values like ABORT_ERR; I'm using the definitions from * the Directories and System spec since it seems to be the most @@ -359,17 +486,18 @@ NativeFileSystem.FileError = function(code) { this.code = code || 0; }; -$.extend(FileError, { - NOT_FOUND_ERR: 1, - SECURITY_ERR: 2, - ABORT_ERR: 3, - NOT_READABLE_ERR: 4, - ENCODING_ERR: 5, - NO_MODIFICATION_ALLOWED_ERR: 6, - INVALID_STATE_ERR: 7, - SYNTAX_ERR: 8, - INVALID_MODIFICATION_ERR: 9, - QUOTA_EXCEEDED_ERR: 10, - TYPE_MISMATCH_ERR: 11, - PATH_EXISTS_ERR: 12 +// FileError constants +Object.defineProperties(FileError, + { NOT_FOUND_ERR: { value: 1 } + , SECURITY_ERR: { value: 2 } + , ABORT_ERR: { value: 3 } + , NOT_READABLE_ERR: { value: 4 } + , ENCODING_ERR: { value: 5 } + , NO_MODIFICATION_ALLOWED_ERR: { value: 6 } + , INVALID_STATE_ERR: { value: 7 } + , SYNTAX_ERR: { value: 8 } + , INVALID_MODIFICATION_ERR: { value: 9 } + , QUOTA_EXCEEDED_ERR: { value: 10 } + , TYPE_MISMATCH_ERR: { value: 11 } + , PATH_EXISTS_ERR: { value: 12 } }); From 318c54ddfe03abe7ffcef6a8803fce0fcdd9b75e Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 15 Dec 2011 14:31:18 -0800 Subject: [PATCH 05/13] Fix NativeFileSystem unit tests --- test/spec/NativeFileSystem-test.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 0b2146c0f46..d0754f6ee1c 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -31,7 +31,7 @@ describe("NativeFileSystem", function() { var entries = null; var readComplete = false; - var nfs = window.NativeFileSystem.requestNativeFileSystem(this.path, requestNativeFileSystemSuccessCB); + var nfs = NativeFileSystem.requestNativeFileSystem(this.path, requestNativeFileSystemSuccessCB); function requestNativeFileSystemSuccessCB( nfs ){ var reader = nfs.createReader(); @@ -53,7 +53,7 @@ describe("NativeFileSystem", function() { it("should return an error if the directory doesn't exist", function() { var successCalled = false, errorCalled = false, error = null; - window.NativeFileSystem.requestNativeFileSystem(this.path + '/nonexistent-dir', function(data) { + NativeFileSystem.requestNativeFileSystem(this.path + '/nonexistent-dir', function(data) { successCalled = true; }, function(err) { errorCalled = true; @@ -71,7 +71,7 @@ describe("NativeFileSystem", function() { it("should return an error if you pass a bad parameter", function() { var successCalled = false, errorCalled = false, error = null; - window.NativeFileSystem.requestNativeFileSystem(0xDEADBEEF, function(data) { + NativeFileSystem.requestNativeFileSystem(0xDEADBEEF, function(data) { successCalled = true; }, function(err) { errorCalled = true; @@ -89,7 +89,7 @@ describe("NativeFileSystem", function() { it("should be okay to not pass an error callback", function() { var entries = null; - window.NativeFileSystem.requestNativeFileSystem(this.path, function(data) { + NativeFileSystem.requestNativeFileSystem(this.path, function(data) { entries = data; }); @@ -106,7 +106,7 @@ describe("NativeFileSystem", function() { it("should write new files", function() { var nfs = null; - window.NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { + NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { nfs = fs; }); From 503a3e0de98e47895fa9bb64d90c2c3720b37cc5 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 15 Dec 2011 21:31:31 -0800 Subject: [PATCH 06/13] Change to brackets.fs error constants --- src/NativeFileSystem.js | 31 +++++++++++------------------- test/spec/NativeFileSystem-test.js | 3 ++- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 9f41baf66ea..f06266b06f1 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -163,15 +163,6 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e }; _FileWriter.prototype.write = function( data ) { - brackets.fs.writeFile(baseDir + "write_test.txt", contents, "utf8", function(err) { - expect(err).toBeFalsy(); - - // Read contents to verify - brackets.fs.readFile(baseDir + "write_test.txt", "utf8", function(err, data) { - expect(err).toBeFalsy(); - expect(data).toBe(contents); - }); - }); }; _FileWriter.prototype.seek = function( offset ) { @@ -189,12 +180,12 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e * @param {Blob} data * @constructor */ -FileSaver = function( data ) { +NativeFileSystem.FileSaver = function( data ) { _data = data; }; // FileSaver constants -Object.defineProperties(FileSaver, +Object.defineProperties(NativeFileSystem.FileSaver, { INIT: { value: 1 } , WRITING: { value: 2 } , DONE: { value: 3 } @@ -202,7 +193,7 @@ Object.defineProperties(FileSaver, // FileSaver private memeber vars NativeFileSystem.FileSaver.prototype._data = null; -NativeFileSystem.FileSaver.prototype._readyState = FileSaver.INIT; +NativeFileSystem.FileSaver.prototype._readyState = NativeFileSystem.FileSaver.INIT; NativeFileSystem.FileSaver.prototype._error = null; // FileSaver methods @@ -281,15 +272,15 @@ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, suc // Use stat() to check if file exists brackets.fs.stat( fileFullPath, function( err, stats ) { if ( options.create ) { - if ( ( options.exclusive && ( err !== FileError.ERR_NOT_FOUND ) ) { + if ( options.exclusive && ( err !== brackets.fs.ERR_NOT_FOUND ) ) { // throw error if file already exists - errorCallback( new FileError( FileError.PATH_EXISTS_ERR ) ); + errorCallback( NativeFileSystem._nativeToFileError( brackets.fs.PATH_EXISTS_ERR ) ); return; } - if ( err === FileError.ERR_NOT_FOUND ) { + if ( err === brackets.fs.ERR_NOT_FOUND ) { brackets.fs.writeFile( fileFullPath, "utf8", "", function( err ) { if ( err ) - errorCallback( new FileError( err ) ); + errorCallback( NativeFileSystem._nativeToFileError( err ) ); else successCallback( new FileEntry( fileFullPath ) ); }); @@ -299,11 +290,11 @@ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, suc } else { // file does not exist - if ( err === FileError.ERR_NOT_FOUND ) - errorCallback( new FileError( FileError.ERR_NOT_FOUND ) ); + if ( err === brackets.fs.ERR_NOT_FOUND ) + errorCallback( NativeFileSystem._nativeToFileError( err ) ); // path is a directory and not a file else if ( stats.isDirectory ) - errorCallback( new FileError( FileError.TYPE_MISMATCH_ERR ) ); + errorCallback( NativeFileSystem._nativeToFileError( err ) ); else successCallback( new FileEntry( fileFullPath ) ); } @@ -487,7 +478,7 @@ NativeFileSystem.FileError = function(code) { }; // FileError constants -Object.defineProperties(FileError, +Object.defineProperties(NativeFileSystem.FileError, { NOT_FOUND_ERR: { value: 1 } , SECURITY_ERR: { value: 2 } , ABORT_ERR: { value: 3 } diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index d0754f6ee1c..dfa5f4b8ad0 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -122,7 +122,8 @@ describe("NativeFileSystem", function() { } var errorCallback = function() { writeComplete = true }; - nfs.root.getFile("should-write-new-files.txt", { create: true }, successCallback, errorCallback ); + // FIXME (jasonsj): NativeFileSystem.root is missing + nfs.getFile("should-write-new-files.txt", { create: true }, successCallback, errorCallback ); }); waitsFor( function() { return writeComplete; }, 1000 ); From 90df2e7ee2ef345d87a452a5ec1be797104ed99c Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 15 Dec 2011 22:14:44 -0800 Subject: [PATCH 07/13] Fixed getFile() to pass correct encoding --- src/NativeFileSystem.js | 253 ++++++++++++++--------------- test/spec/NativeFileSystem-test.js | 12 +- 2 files changed, 134 insertions(+), 131 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 480b907eca4..7c029403102 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -1,50 +1,49 @@ /* - * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved. - */ +* Copyright 2011 Adobe Systems Incorporated. All Rights Reserved. +*/ var NativeFileSystem = { - /** showOpenDialog - * - * @param {bool} allowMultipleSelection - * @param {bool} chooseDirectories - * @param {string} title - * @param {string} initialPath - * @param {string[]} fileTypes - * @param {function} resultCallback - * @constructor - */ + * + * @param {bool} allowMultipleSelection + * @param {bool} chooseDirectories + * @param {string} title + * @param {string} initialPath + * @param {string[]} fileTypes + * @param {function} resultCallback + * @constructor + */ showOpenDialog: function ( allowMultipleSelection, - chooseDirectories, - title, - initialPath, - fileTypes, - successCallback, - errorCallback ) { + chooseDirectories, + title, + initialPath, + fileTypes, + successCallback, + errorCallback ) + { if( !successCallback ) - return; + return; var files = brackets.fs.showOpenDialog( allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, - function( err, data ){ + function( err, data ) { if( ! err ) - successCallback( data ); + successCallback( data ); else if (errorCallback) - errorCallback(NativeFileSystem._nativeToFileError(err)); + errorCallback(NativeFileSystem._nativeToFileError(err)); }); - }, /** requestNativeFileSystem - * - * @param {string} path - * @param {function} successCallback - * @param {function} errorCallback - */ + * + * @param {string} path + * @param {function} successCallback + * @param {function} errorCallback + */ requestNativeFileSystem: function( path, successCallback, errorCallback ){ brackets.fs.stat(path, function( err, data ){ if( !err ){ @@ -67,38 +66,38 @@ var NativeFileSystem = { // since there aren't specific mappings for these. case brackets.fs.ERR_UNKNOWN: case brackets.fs.ERR_INVALID_PARAMS: - error = FileError.SECURITY_ERR; - break; + error = FileError.SECURITY_ERR; + break; case brackets.fs.ERR_NOT_FOUND: - error = FileError.NOT_FOUND_ERR; - break; + error = FileError.NOT_FOUND_ERR; + break; case brackets.fs.ERR_CANT_READ: - error = FileError.NOT_READABLE_ERR; - break; + error = FileError.NOT_READABLE_ERR; + break; // It might seem like you should use FileError.ENCODING_ERR for this, // but according to the spec that's for malformed URLs. case brackets.fs.ERR_UNSUPPORTED_ENCODING: - error = FileError.SECURITY_ERR; - break; + error = FileError.SECURITY_ERR; + break; case brackets.fs.ERR_CANT_WRITE: - error = FileError.NO_MODIFICATION_ALLOWED_ERR; - break; + error = FileError.NO_MODIFICATION_ALLOWED_ERR; + break; case brackets.fs.ERR_OUT_OF_SPACE: - error = FileError.QUOTA_EXCEEDED_ERR; - break; + error = FileError.QUOTA_EXCEEDED_ERR; + break; } return new NativeFileSystem.FileError(error); } }; /** class: Entry - * - * @param {string} name - * @param {string} isFile - * @constructor - */ +* +* @param {string} name +* @param {string} isFile +* @constructor +*/ NativeFileSystem.Entry = function( fullPath, isDirectory) { this.isDirectory = isDirectory; this.isFile = !isDirectory; @@ -110,7 +109,7 @@ NativeFileSystem.Entry = function( fullPath, isDirectory) { if( fullPath ){ var pathParts = fullPath.split( "/" ); if( pathParts.length > 0 ) - this.name = pathParts.pop(); + this.name = pathParts.pop(); } // IMPLEMENT LATER var filesystem; @@ -124,11 +123,11 @@ NativeFileSystem.Entry = function( fullPath, isDirectory) { /** class: FileEntry - * - * @param {string} name - * @constructor - * @extends {Entry} - */ +* +* @param {string} name +* @constructor +* @extends {Entry} +*/ NativeFileSystem.FileEntry = function( name ) { NativeFileSystem.Entry.call(this, name, false); @@ -137,12 +136,12 @@ NativeFileSystem.FileEntry = function( name ) { }; /** createWriter - * - Creates a new FileWriter associated with the file that this FileEntry represents. - * - * @param {function} successCallback - * @param {function} errorCallback - */ +* +Creates a new FileWriter associated with the file that this FileEntry represents. +* +* @param {function} successCallback +* @param {function} errorCallback +*/ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, errorCallback ) { _FileWriter = function( data ) { FileSaver.call(this, data); @@ -174,10 +173,10 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e }; /** class: FileSaver - * - * @param {Blob} data - * @constructor - */ +* +* @param {Blob} data +* @constructor +*/ NativeFileSystem.FileSaver = function( data ) { _data = data; }; @@ -200,7 +199,7 @@ NativeFileSystem.FileSaver.prototype._error = null; NativeFileSystem.FileSaver.prototype.abort = function() { // If readyState is DONE or INIT, terminate this overall series of steps without doing anything else.. if (_readyState == FileSaver.INIT || _readyState == FileSaver.DONE) - return; + return; // Terminate any steps having to do with writing a file. @@ -219,12 +218,12 @@ NativeFileSystem.FileSaver.prototype.abort = function() { }; /** file - * - * Obtains the File objecte for a FileEntry object - * - * @param {function} successCallback - * @param {function} errorCallback - */ +* +* Obtains the File objecte for a FileEntry object +* +* @param {function} successCallback +* @param {function} errorCallback +*/ NativeFileSystem.FileEntry.prototype.file = function( successCallback, errorCallback ){ var newFile = new NativeFileSystem.File( this ); successCallback( newFile ); @@ -236,15 +235,15 @@ NativeFileSystem.FileEntry.prototype.file = function( successCallback, errorCall /* TODO Jason NativeFileSystem.FileEntry.prototype.createfileerror = function( successCallback, errorCallback ){ - -}; */ +}; +*/ /** class: DirectoryEntry - * - * @constructor - * @param {string} name - * @extends {Entry} - */ +* +* @constructor +* @param {string} name +* @extends {Entry} +*/ NativeFileSystem.DirectoryEntry = function( name ) { NativeFileSystem.Entry.call(this, name, true); @@ -276,11 +275,11 @@ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, suc return; } if ( err === brackets.fs.ERR_NOT_FOUND ) { - brackets.fs.writeFile( fileFullPath, "utf8", "", function( err ) { + brackets.fs.writeFile( fileFullPath, "", "utf8", function( err ) { if ( err ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + errorCallback( NativeFileSystem._nativeToFileError( err ) ); else - successCallback( new FileEntry( fileFullPath ) ); + successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); }); return; @@ -289,30 +288,30 @@ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, suc else { // file does not exist if ( err === brackets.fs.ERR_NOT_FOUND ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + errorCallback( NativeFileSystem._nativeToFileError( err ) ); // path is a directory and not a file else if ( stats.isDirectory ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + errorCallback( NativeFileSystem._nativeToFileError( err ) ); else - successCallback( new FileEntry( fileFullPath ) ); + successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); } }); }; /** class: DirectoryReader - */ +*/ NativeFileSystem.DirectoryReader = function() { }; /** readEntries - * - * @param {function} successCallback - * @param {function} errorCallback - * @returns {Entry[]} - */ +* +* @param {function} successCallback +* @param {function} errorCallback +* @returns {Entry[]} +*/ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallback, errorCallback ){ var rootPath = this._directory.fullPath; var jsonList = brackets.fs.readdir( rootPath, function( err, filelist ) { @@ -326,9 +325,9 @@ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallba if( !err ){ if( statData.isDirectory() ) - entries.push( new NativeFileSystem.DirectoryEntry( itemFullPath ) ); + entries.push( new NativeFileSystem.DirectoryEntry( itemFullPath ) ); else if( statData.isFile() ) - entries.push( new NativeFileSystem.FileEntry( itemFullPath ) ); + entries.push( new NativeFileSystem.FileEntry( itemFullPath ) ); } else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); @@ -347,9 +346,9 @@ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallba /** class: FileReader - * - * @extends {EventTarget} - */ +* +* @extends {EventTarget} +*/ NativeFileSystem.FileReader = function() { // Todo Ty: this classes should extend EventTarget @@ -384,31 +383,31 @@ NativeFileSystem.FileReader = function() { }; /** readAsText - * - * @param {Blob} blob - * @param {string} encoding - */ +* +* @param {Blob} blob +* @param {string} encoding +*/ NativeFileSystem.FileReader.prototype.readAsText = function( blob, encoding) { var self = this; if( !encoding ) - encoding = "utf-8"; + encoding = "utf-8"; if( this.readyState == this.LOADING ) - throw new InvalidateStateError(); + throw new InvalidateStateError(); this.readyState = this.LOADING; if( this.onloadstart ) - this.onloadstart(); // todo params + this.onloadstart(); // todo params brackets.fs.readFile( blob._fullPath, encoding, function( err, data) { // TODO: the event objects passed to these event handlers is fake and incomplete right now var fakeEvent = - { loaded: 0 + { loaded: 0 , total: 0 - }; + }; // The target for this event is the FileReader and the data/err result is stored in the FileReader fakeEvent.target = this; @@ -430,27 +429,27 @@ NativeFileSystem.FileReader.prototype.readAsText = function( blob, encoding) { fakeEvent.total = 1; if( self.onprogress ) - self.onprogress(fakeEvent); + self.onprogress(fakeEvent); // TODO: this.onabort not currently supported since our native implementation doesn't support it // if( self.onabort ) // self.onabort(fakeEvent); if( self.onload ) - self.onload( fakeEvent ); + self.onload( fakeEvent ); if( self.onloadend ) - self.onloadend(); + self.onloadend(); } }); }; /** class: Blob - * - * @constructor - * param {Entry} entry - */ +* +* @constructor +* param {Entry} entry +*/ NativeFileSystem.Blob = function ( fullPath ){ this._fullPath = fullPath; @@ -465,30 +464,30 @@ NativeFileSystem.Blob = function ( fullPath ){ }; /** class: File - * - * @constructor - * param {Entry} entry - * @extends {Blob} - */ +* +* @constructor +* param {Entry} entry +* @extends {Blob} +*/ NativeFileSystem.File = function ( entry ){ - NativeFileSystem.Blob.call( this, entry.fullPath ); +NativeFileSystem.Blob.call( this, entry.fullPath ); - //IMPLEMENT LATER get name() { return this.entry.name; } - // IMPLEMENT LATER get lastModifiedDate() { return } use stat to get mod date +//IMPLEMENT LATER get name() { return this.entry.name; } +// IMPLEMENT LATER get lastModifiedDate() { return } use stat to get mod date }; /** class: FileError - * - * Implementation of HTML file API error code return class. Note that we don't - * actually define the error codes here--we rely on the browser's built-in FileError - * class's constants. In other words, external clients of this API should always - * use FileError., not NativeFileSystem.FileError.. - * - * @constructor - * @param {number} code The error code to return with this FileError. Must be - * one of the codes defined in the FileError class. - */ +* +* Implementation of HTML file API error code return class. Note that we don't +* actually define the error codes here--we rely on the browser's built-in FileError +* class's constants. In other words, external clients of this API should always +* use FileError., not NativeFileSystem.FileError.. +* +* @constructor +* @param {number} code The error code to return with this FileError. Must be +* one of the codes defined in the FileError class. +*/ NativeFileSystem.FileError = function(code) { - this.code = code || 0; +this.code = code || 0; }; \ No newline at end of file diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 7381890dc8f..624feac0f1d 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -220,7 +220,7 @@ describe("NativeFileSystem", function(){ describe("Writing", function() { - it("should write new files", function() { + it("should create new, zero-length files", function() { var nfs = null; NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { @@ -237,16 +237,20 @@ describe("NativeFileSystem", function(){ fileEntry = entry; writeComplete = true; } - var errorCallback = function() { writeComplete = true }; + var errorCallback = function() { + writeComplete = true + }; // FIXME (jasonsj): NativeFileSystem.root is missing - nfs.getFile("should-write-new-files.txt", { create: true }, successCallback, errorCallback ); + // FIXME (jasonsj): Need brackets.fs.rm() to cleanup created file + // https://github.com/adobe/brackets-app/issues/14 + nfs.getFile(Math.random() + "do-not-commit.txt", { create: true }, successCallback, errorCallback ); }); waitsFor( function() { return writeComplete; }, 1000 ); runs(function() { - expect(entries).not.toBe(null); + expect(fileEntry).not.toBe(null); }); }); From 9dee2302c0c3d42c4b1b890ca1e2ec442d320d7b Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 15 Dec 2011 22:25:25 -0800 Subject: [PATCH 08/13] Fix indentation --- src/NativeFileSystem.js | 214 ++++++++++++++++++++-------------------- 1 file changed, 106 insertions(+), 108 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 7c029403102..4ab4efa2997 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -1,29 +1,29 @@ /* -* Copyright 2011 Adobe Systems Incorporated. All Rights Reserved. -*/ + * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved. + */ var NativeFileSystem = { /** showOpenDialog - * - * @param {bool} allowMultipleSelection - * @param {bool} chooseDirectories - * @param {string} title - * @param {string} initialPath - * @param {string[]} fileTypes - * @param {function} resultCallback - * @constructor - */ + * + * @param {bool} allowMultipleSelection + * @param {bool} chooseDirectories + * @param {string} title + * @param {string} initialPath + * @param {string[]} fileTypes + * @param {function} resultCallback + * @constructor + */ showOpenDialog: function ( allowMultipleSelection, - chooseDirectories, - title, - initialPath, - fileTypes, - successCallback, - errorCallback ) + chooseDirectories, + title, + initialPath, + fileTypes, + successCallback, + errorCallback ) { if( !successCallback ) - return; + return; var files = brackets.fs.showOpenDialog( allowMultipleSelection, chooseDirectories, @@ -32,18 +32,18 @@ var NativeFileSystem = { fileTypes, function( err, data ) { if( ! err ) - successCallback( data ); + successCallback( data ); else if (errorCallback) - errorCallback(NativeFileSystem._nativeToFileError(err)); + errorCallback(NativeFileSystem._nativeToFileError(err)); }); }, /** requestNativeFileSystem - * - * @param {string} path - * @param {function} successCallback - * @param {function} errorCallback - */ + * + * @param {string} path + * @param {function} successCallback + * @param {function} errorCallback + */ requestNativeFileSystem: function( path, successCallback, errorCallback ){ brackets.fs.stat(path, function( err, data ){ if( !err ){ @@ -66,38 +66,36 @@ var NativeFileSystem = { // since there aren't specific mappings for these. case brackets.fs.ERR_UNKNOWN: case brackets.fs.ERR_INVALID_PARAMS: - error = FileError.SECURITY_ERR; - break; + error = FileError.SECURITY_ERR; + break; case brackets.fs.ERR_NOT_FOUND: - error = FileError.NOT_FOUND_ERR; - break; + error = FileError.NOT_FOUND_ERR; + break; case brackets.fs.ERR_CANT_READ: - error = FileError.NOT_READABLE_ERR; - break; - + error = FileError.NOT_READABLE_ERR; + break; // It might seem like you should use FileError.ENCODING_ERR for this, // but according to the spec that's for malformed URLs. case brackets.fs.ERR_UNSUPPORTED_ENCODING: - error = FileError.SECURITY_ERR; - break; - + error = FileError.SECURITY_ERR; + break; case brackets.fs.ERR_CANT_WRITE: - error = FileError.NO_MODIFICATION_ALLOWED_ERR; - break; + error = FileError.NO_MODIFICATION_ALLOWED_ERR; + break; case brackets.fs.ERR_OUT_OF_SPACE: - error = FileError.QUOTA_EXCEEDED_ERR; - break; + error = FileError.QUOTA_EXCEEDED_ERR; + break; } return new NativeFileSystem.FileError(error); } }; /** class: Entry -* -* @param {string} name -* @param {string} isFile -* @constructor -*/ + * + * @param {string} name + * @param {string} isFile + * @constructor + */ NativeFileSystem.Entry = function( fullPath, isDirectory) { this.isDirectory = isDirectory; this.isFile = !isDirectory; @@ -123,11 +121,11 @@ NativeFileSystem.Entry = function( fullPath, isDirectory) { /** class: FileEntry -* -* @param {string} name -* @constructor -* @extends {Entry} -*/ + * + * @param {string} name + * @constructor + * @extends {Entry} + */ NativeFileSystem.FileEntry = function( name ) { NativeFileSystem.Entry.call(this, name, false); @@ -136,12 +134,12 @@ NativeFileSystem.FileEntry = function( name ) { }; /** createWriter -* -Creates a new FileWriter associated with the file that this FileEntry represents. -* -* @param {function} successCallback -* @param {function} errorCallback -*/ + * + * Creates a new FileWriter associated with the file that this FileEntry represents. + * + * @param {function} successCallback + * @param {function} errorCallback + */ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, errorCallback ) { _FileWriter = function( data ) { FileSaver.call(this, data); @@ -218,12 +216,12 @@ NativeFileSystem.FileSaver.prototype.abort = function() { }; /** file -* -* Obtains the File objecte for a FileEntry object -* -* @param {function} successCallback -* @param {function} errorCallback -*/ + * + * Obtains the File objecte for a FileEntry object + * + * @param {function} successCallback + * @param {function} errorCallback + */ NativeFileSystem.FileEntry.prototype.file = function( successCallback, errorCallback ){ var newFile = new NativeFileSystem.File( this ); successCallback( newFile ); @@ -239,11 +237,11 @@ NativeFileSystem.FileEntry.prototype.createfileerror = function( successCallback */ /** class: DirectoryEntry -* -* @constructor -* @param {string} name -* @extends {Entry} -*/ + * + * @constructor + * @param {string} name + * @extends {Entry} + */ NativeFileSystem.DirectoryEntry = function( name ) { NativeFileSystem.Entry.call(this, name, true); @@ -307,11 +305,11 @@ NativeFileSystem.DirectoryReader = function() { /** readEntries -* -* @param {function} successCallback -* @param {function} errorCallback -* @returns {Entry[]} -*/ + * + * @param {function} successCallback + * @param {function} errorCallback + * @returns {Entry[]} + */ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallback, errorCallback ){ var rootPath = this._directory.fullPath; var jsonList = brackets.fs.readdir( rootPath, function( err, filelist ) { @@ -325,9 +323,9 @@ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallba if( !err ){ if( statData.isDirectory() ) - entries.push( new NativeFileSystem.DirectoryEntry( itemFullPath ) ); + entries.push( new NativeFileSystem.DirectoryEntry( itemFullPath ) ); else if( statData.isFile() ) - entries.push( new NativeFileSystem.FileEntry( itemFullPath ) ); + entries.push( new NativeFileSystem.FileEntry( itemFullPath ) ); } else if (errorCallback) { errorCallback(NativeFileSystem._nativeToFileError(err)); @@ -346,9 +344,9 @@ NativeFileSystem.DirectoryReader.prototype.readEntries = function( successCallba /** class: FileReader -* -* @extends {EventTarget} -*/ + * + * @extends {EventTarget} + */ NativeFileSystem.FileReader = function() { // Todo Ty: this classes should extend EventTarget @@ -383,31 +381,31 @@ NativeFileSystem.FileReader = function() { }; /** readAsText -* -* @param {Blob} blob -* @param {string} encoding -*/ + * + * @param {Blob} blob + * @param {string} encoding + */ NativeFileSystem.FileReader.prototype.readAsText = function( blob, encoding) { var self = this; if( !encoding ) - encoding = "utf-8"; + encoding = "utf-8"; if( this.readyState == this.LOADING ) - throw new InvalidateStateError(); + throw new InvalidateStateError(); this.readyState = this.LOADING; if( this.onloadstart ) - this.onloadstart(); // todo params + this.onloadstart(); // todo params brackets.fs.readFile( blob._fullPath, encoding, function( err, data) { // TODO: the event objects passed to these event handlers is fake and incomplete right now var fakeEvent = - { loaded: 0 + { loaded: 0 , total: 0 - }; + }; // The target for this event is the FileReader and the data/err result is stored in the FileReader fakeEvent.target = this; @@ -429,17 +427,17 @@ NativeFileSystem.FileReader.prototype.readAsText = function( blob, encoding) { fakeEvent.total = 1; if( self.onprogress ) - self.onprogress(fakeEvent); + self.onprogress(fakeEvent); // TODO: this.onabort not currently supported since our native implementation doesn't support it // if( self.onabort ) // self.onabort(fakeEvent); if( self.onload ) - self.onload( fakeEvent ); + self.onload( fakeEvent ); if( self.onloadend ) - self.onloadend(); + self.onloadend(); } }); @@ -464,30 +462,30 @@ NativeFileSystem.Blob = function ( fullPath ){ }; /** class: File -* -* @constructor -* param {Entry} entry -* @extends {Blob} -*/ + * + * @constructor + * param {Entry} entry + * @extends {Blob} + */ NativeFileSystem.File = function ( entry ){ -NativeFileSystem.Blob.call( this, entry.fullPath ); + NativeFileSystem.Blob.call( this, entry.fullPath ); -//IMPLEMENT LATER get name() { return this.entry.name; } -// IMPLEMENT LATER get lastModifiedDate() { return } use stat to get mod date + // IMPLEMENT LATER get name() { return this.entry.name; } + // IMPLEMENT LATER get lastModifiedDate() { return } use stat to get mod date }; /** class: FileError -* -* Implementation of HTML file API error code return class. Note that we don't -* actually define the error codes here--we rely on the browser's built-in FileError -* class's constants. In other words, external clients of this API should always -* use FileError., not NativeFileSystem.FileError.. -* -* @constructor -* @param {number} code The error code to return with this FileError. Must be -* one of the codes defined in the FileError class. -*/ -NativeFileSystem.FileError = function(code) { -this.code = code || 0; + * + * Implementation of HTML file API error code return class. Note that we don't + * actually define the error codes here--we rely on the browser's built-in FileError + * class's constants. In other words, external clients of this API should always + * use FileError., not NativeFileSystem.FileError.. + * + * @constructor + * @param {number} code The error code to return with this FileError. Must be + * one of the codes defined in the FileError class. + */ +NativeFileSystem.FileError = function( code ) { + this.code = code || 0; }; \ No newline at end of file From e62be1925b9cf1187526b5b17b982857872744d0 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Thu, 15 Dec 2011 23:44:08 -0800 Subject: [PATCH 09/13] _FileWriter bug fixes --- src/NativeFileSystem.js | 106 +++++++++++++++++++++++------ test/spec/NativeFileSystem-test.js | 77 ++++++++++++++++++--- 2 files changed, 156 insertions(+), 27 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 4ab4efa2997..7415cd5752f 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -107,7 +107,7 @@ NativeFileSystem.Entry = function( fullPath, isDirectory) { if( fullPath ){ var pathParts = fullPath.split( "/" ); if( pathParts.length > 0 ) - this.name = pathParts.pop(); + this.name = pathParts.pop(); } // IMPLEMENT LATER var filesystem; @@ -141,8 +141,19 @@ NativeFileSystem.FileEntry = function( name ) { * @param {function} errorCallback */ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, errorCallback ) { + var fileEntry = this; + + // [NoInterfaceObject] + // interface FileWriter : FileSaver _FileWriter = function( data ) { - FileSaver.call(this, data); + NativeFileSystem.FileSaver.call(this, data); + + // initialize file length + // TODO (jasonsj): handle async + brackets.fs.readFile( fileEntry.fullPath, "utf8", function(err, contents) { + if ( contents ) + this._length = contents.length; + }); }; // FileWriter private memeber vars @@ -157,7 +168,47 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e return this._position; }; + // TODO (jasonsj): handle Blob data instead of string _FileWriter.prototype.write = function( data ) { + // TODO (jasonsj): data is required + if ( !data ) + throw new Error(); + + if ( this.readyState === NativeFileSystem.FileSaver.WRITING ) + throw new NativeFileSystem.FileException( NativeFileSystem.FileException.INVALID_STATE_ERR ); + + this.readyState = NativeFileSystem.FileSaver.WRITING; + + if ( this.onwritestart ) { + // TODO (jasonsj): progressevent + this.onwritestart(); + } + + brackets.fs.writeFile( fileEntry.fullPath, data, "utf8", function( err ) { + // FIXME (jasonsj): why is this === window? + if ( this.onerror ) { + this.onerror ( NativeFileSystem._nativeToFileError( err ) ); + + // TODO (jasonsj): partial write, update length and position + } + else { + // successful completetion of a write + this.position += data.size; + } + + // DONE is set regardless of error + this.readyState = NativeFileSystem.FileSaver.DONE; + + if ( this.onwrite ) { + // TODO (jasonsj): progressevent + this.onwrite(); + } + + if ( this.onwriteend ) { + // TODO (jasonsj): progressevent + this.onwriteend(); + } + }); }; _FileWriter.prototype.seek = function( offset ) { @@ -166,15 +217,32 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e _FileWriter.prototype.truncate = function( size ) { }; - var fileWriter = new _FileWriter(); - successCallback( fileWriter ); + successCallback( new _FileWriter() ); }; + +NativeFileSystem.FileException = function ( code ){ + this.code = code || 0; +}; + +// FileException constants +Object.defineProperties(NativeFileSystem.FileException, + { NOT_FOUND_ERR: { value: 1 } + , SECURITY_ERR: { value: 2 } + , ABORT_ERR: { value: 3 } + , NOT_READABLE_ERR: { value: 4 } + , ENCODING_ERR: { value: 5 } + , NO_MODIFICATION_ALLOWED_ERR: { value: 6 } + , INVALID_STATE_ERR: { value: 7 } + , SYNTAX_ERR: { value: 8 } + , QUOTA_EXCEEDED_ERR: { value: 10 } +}); + /** class: FileSaver -* -* @param {Blob} data -* @constructor -*/ + * + * @param {Blob} data + * @constructor + */ NativeFileSystem.FileSaver = function( data ) { _data = data; }; @@ -275,9 +343,9 @@ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, suc if ( err === brackets.fs.ERR_NOT_FOUND ) { brackets.fs.writeFile( fileFullPath, "", "utf8", function( err ) { if ( err ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + errorCallback( NativeFileSystem._nativeToFileError( err ) ); else - successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); + successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); }); return; @@ -286,19 +354,19 @@ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, suc else { // file does not exist if ( err === brackets.fs.ERR_NOT_FOUND ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + errorCallback( NativeFileSystem._nativeToFileError( err ) ); // path is a directory and not a file - else if ( stats.isDirectory ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + else if ( stats.isDirectory() ) + errorCallback( NativeFileSystem._nativeToFileError( err ) ); else - successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); + successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); } }); }; /** class: DirectoryReader -*/ + */ NativeFileSystem.DirectoryReader = function() { }; @@ -444,10 +512,10 @@ NativeFileSystem.FileReader.prototype.readAsText = function( blob, encoding) { }; /** class: Blob -* -* @constructor -* param {Entry} entry -*/ + * + * @constructor + * param {Entry} entry + */ NativeFileSystem.Blob = function ( fullPath ){ this._fullPath = fullPath; diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 624feac0f1d..79fe78376b6 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -2,6 +2,7 @@ describe("NativeFileSystem", function(){ beforeEach(function() { this.path = SpecRunnerUtils.getTestPath("/spec/NativeFileSystem-test-files"); + this.file1content = "Here is file1\n"; }); describe("Reading a directory", function() { @@ -124,7 +125,7 @@ describe("NativeFileSystem", function(){ expect(gotFile).toBe(true); expect(readFile).toBe(true); expect(gotError).toBe(false); - expect(content).toBe("Here is file1\n"); + expect(content).toBe(this.file1content); }); }); @@ -220,6 +221,14 @@ describe("NativeFileSystem", function(){ describe("Writing", function() { + beforeEach( function() { + }); + + afterEach( function() { + // reset file1 content + brackets.fs.writeFile( this.path + "/file1", this.file1content, "utf8" ); + }); + it("should create new, zero-length files", function() { var nfs = null; @@ -238,7 +247,7 @@ describe("NativeFileSystem", function(){ writeComplete = true; } var errorCallback = function() { - writeComplete = true + writeComplete = true; }; // FIXME (jasonsj): NativeFileSystem.root is missing @@ -254,16 +263,68 @@ describe("NativeFileSystem", function(){ }); }); - it("should append to existing files", function() { - this.fail("TODO"); + it("should create overwrite files with new content", function() { + var nfs = null; + + NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { + nfs = fs; + }); + + waitsFor( function() { return nfs }, 1000); + + var fileEntry = null; + var writeComplete = false; + + runs(function() { + var successCallback = function( entry ) { + fileEntry = entry; + + fileEntry.createWriter( function ( fileWriter ) { + fileWriter.onwriteend = function( e ) { + writeComplete = true; + }; + fileWriter.onerror = function( e ) { + writeComplete = true; + }; + + // TODO (jasonsj): BlobBulder + fileWriter.write( "FileWriter.write" ); + }); + } + var errorCallback = function() { + writeComplete = true; + }; + + nfs.getFile( "file1", { create: false }, successCallback, errorCallback ); + }); + + waitsFor( function() { return writeComplete && fileEntry; }, 1000 ); + + var actualContents = null; + + runs(function() { + brackets.fs.readFile( fileEntry.fullPath, "utf8", function ( err, contents ) { + actualContents = contents; + }); + }); + + waitsFor( function() { return !!contents; }, 1000 ); + + runs(function() { + expect(actualContents).toEqual("FileWriter.write"); + }); + }); + + xit("should append to existing files", function() { + this.fail("TODO (jasonsj): not supported for sprint 1"); }); - it("should seek into a file before writing", function() { - this.fail("TODO"); + xit("should seek into a file before writing", function() { + this.fail("TODO (jasonsj): not supported for sprint 1"); }); - it("should truncate files", function() { - this.fail("TODO"); + xit("should truncate files", function() { + this.fail("TODO (jasonsj): not supported for sprint 1"); }); }); }); From 26a603b6d8beeccf1957f260e726f44a84a60432 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 16 Dec 2011 09:05:09 -0800 Subject: [PATCH 10/13] Fix _FileWriter event handling --- src/NativeFileSystem.js | 18 +++++++++--------- test/spec/NativeFileSystem-test.js | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 7415cd5752f..0ec1411166e 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -145,7 +145,7 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e // [NoInterfaceObject] // interface FileWriter : FileSaver - _FileWriter = function( data ) { + var _FileWriter = function( data ) { NativeFileSystem.FileSaver.call(this, data); // initialize file length @@ -170,7 +170,6 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e // TODO (jasonsj): handle Blob data instead of string _FileWriter.prototype.write = function( data ) { - // TODO (jasonsj): data is required if ( !data ) throw new Error(); @@ -184,29 +183,30 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e this.onwritestart(); } + var self = this; + brackets.fs.writeFile( fileEntry.fullPath, data, "utf8", function( err ) { - // FIXME (jasonsj): why is this === window? - if ( this.onerror ) { - this.onerror ( NativeFileSystem._nativeToFileError( err ) ); + if ( self.onerror ) { + self.onerror ( NativeFileSystem._nativeToFileError( err ) ); // TODO (jasonsj): partial write, update length and position } else { // successful completetion of a write - this.position += data.size; + self.position += data.size; } // DONE is set regardless of error this.readyState = NativeFileSystem.FileSaver.DONE; - if ( this.onwrite ) { + if ( self.onwrite ) { // TODO (jasonsj): progressevent - this.onwrite(); + self.onwrite(); } if ( this.onwriteend ) { // TODO (jasonsj): progressevent - this.onwriteend(); + self.onwriteend(); } }); }; diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 79fe78376b6..1520ada6cf8 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -308,7 +308,7 @@ describe("NativeFileSystem", function(){ }); }); - waitsFor( function() { return !!contents; }, 1000 ); + waitsFor( function() { return !!actualContents; }, 1000 ); runs(function() { expect(actualContents).toEqual("FileWriter.write"); From 049436c81b700bef053a97be5addc1f3bb917cee Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 16 Dec 2011 10:02:20 -0800 Subject: [PATCH 11/13] Code review from NJ. Move private members out of prototype to constructor. --- src/NativeFileSystem.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 0ec1411166e..66cb840b49c 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -148,6 +148,10 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e var _FileWriter = function( data ) { NativeFileSystem.FileSaver.call(this, data); + // _FileWriter private memeber vars + this._length = 0; + this._position = 0; + // initialize file length // TODO (jasonsj): handle async brackets.fs.readFile( fileEntry.fullPath, "utf8", function(err, contents) { @@ -156,10 +160,6 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e }); }; - // FileWriter private memeber vars - _FileWriter.prototype._length = 0; - _FileWriter.prototype._position = 0; - _FileWriter.prototype.length = function( ) { return this._length; }; @@ -192,7 +192,7 @@ NativeFileSystem.FileEntry.prototype.createWriter = function( successCallback, e // TODO (jasonsj): partial write, update length and position } else { - // successful completetion of a write + // successful completion of a write self.position += data.size; } @@ -244,7 +244,10 @@ Object.defineProperties(NativeFileSystem.FileException, * @constructor */ NativeFileSystem.FileSaver = function( data ) { - _data = data; + // FileSaver private member vars + this._data = data; + this._readyState = NativeFileSystem.FileSaver.INIT; + this._error = null; }; // FileSaver constants @@ -254,11 +257,6 @@ Object.defineProperties(NativeFileSystem.FileSaver, , DONE: { value: 3 } }); -// FileSaver private memeber vars -NativeFileSystem.FileSaver.prototype._data = null; -NativeFileSystem.FileSaver.prototype._readyState = NativeFileSystem.FileSaver.INIT; -NativeFileSystem.FileSaver.prototype._error = null; - // FileSaver methods // TODO (jasonsj): http://dev.w3.org/2009/dap/file-system/file-writer.html#widl-FileSaver-abort-void @@ -270,7 +268,7 @@ NativeFileSystem.FileSaver.prototype.abort = function() { // Terminate any steps having to do with writing a file. // Set the error attribute to a FileError object with the code ABORT_ERR. - _error = new FileError(FileError.ABORT_ERR); + _error = new NativeFileSystem.FileError(FileError.ABORT_ERR); // Set readyState to DONE. _readyState = FileSaver.DONE; From 9e39016a15df23e0bb8a42d2df873db76f92afd1 Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 16 Dec 2011 11:48:16 -0800 Subject: [PATCH 12/13] Fix getFile() error when path is a directory. More getFile() error handling unit tests. Use fs.unlink() to remove files created during unit tests. --- src/NativeFileSystem.js | 5 +- test/spec/NativeFileSystem-test.js | 155 +++++++++++++++++++++++++++-- 2 files changed, 153 insertions(+), 7 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 66cb840b49c..32cad0377c7 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -85,6 +85,9 @@ var NativeFileSystem = { case brackets.fs.ERR_OUT_OF_SPACE: error = FileError.QUOTA_EXCEEDED_ERR; break; + case brackets.fs.PATH_EXISTS_ERR: + error = FileError.PATH_EXISTS_ERR; + break; } return new NativeFileSystem.FileError(error); } @@ -355,7 +358,7 @@ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, suc errorCallback( NativeFileSystem._nativeToFileError( err ) ); // path is a directory and not a file else if ( stats.isDirectory() ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + errorCallback( new NativeFileSystem.FileError( FileError.TYPE_MISMATCH_ERR ) ); else successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); } diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 1520ada6cf8..f43f242bcc2 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -225,8 +225,6 @@ describe("NativeFileSystem", function(){ }); afterEach( function() { - // reset file1 content - brackets.fs.writeFile( this.path + "/file1", this.file1content, "utf8" ); }); it("should create new, zero-length files", function() { @@ -241,6 +239,7 @@ describe("NativeFileSystem", function(){ var fileEntry = null; var writeComplete = false; + // create a new file exclusively runs(function() { var successCallback = function( entry ) { fileEntry = entry; @@ -251,16 +250,156 @@ describe("NativeFileSystem", function(){ }; // FIXME (jasonsj): NativeFileSystem.root is missing - // FIXME (jasonsj): Need brackets.fs.rm() to cleanup created file - // https://github.com/adobe/brackets-app/issues/14 - nfs.getFile(Math.random() + "do-not-commit.txt", { create: true }, successCallback, errorCallback ); + nfs.getFile("new-zero-length-file.txt", { create: true, exclusive: true }, successCallback, errorCallback ); }); waitsFor( function() { return writeComplete; }, 1000 ); + // fileEntry is non-null on success runs(function() { expect(fileEntry).not.toBe(null); }); + + var actualContents = null; + + // read the new file + runs(function() { + brackets.fs.readFile( fileEntry.fullPath, "utf8", function ( err, contents ) { + actualContents = contents; + }); + }); + + // wait for content to be read + waitsFor( function() { return (actualContents !== null); }, 1000 ); + + // verify actual content to be empty + runs(function() { + expect(actualContents).toEqual(""); + + // cleanup + var self = this; + brackets.fs.unlink(fileEntry.fullPath, function( err ) { + if ( err !== brackets.fs.NO_ERROR ) + self.fail("Failed to delete " + fileEntry.fullPath); + }); + }); + }); + + it("should report an error when a file does not exist and create = false", function() { + var nfs = null; + + NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { + nfs = fs; + }); + + waitsFor( function() { return nfs }, 1000); + + var fileEntry = null; + var writeComplete = false; + var error = null; + + // create a new file exclusively + runs(function() { + var successCallback = function( entry ) { + fileEntry = entry; + writeComplete = true; + } + var errorCallback = function( err ) { + error = err; + writeComplete = true; + }; + + // FIXME (jasonsj): NativeFileSystem.root is missing + nfs.getFile("does-not-exist.txt", { create: false }, successCallback, errorCallback ); + }); + + waitsFor( function() { return writeComplete; }, 1000 ); + + // fileEntry is null on error + runs(function() { + expect(fileEntry).toBe(null); + expect(error.code).toBe(FileError.NOT_FOUND_ERR) + }); + }); + + it("should return an error if file exists and exclusive is true", function() { + var nfs = null; + + NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { + nfs = fs; + }); + + waitsFor( function() { return nfs }, 1000); + + var fileEntry = null; + var writeComplete = false; + var error = null; + + // try to create a new file exclusively when the file name already exists + runs(function() { + var successCallback = function( entry ) { + fileEntry = entry; + writeComplete = true; + } + var errorCallback = function( err ) { + error = err; + writeComplete = true; + }; + + // FIXME (jasonsj): NativeFileSystem.root is missing + nfs.getFile("file1", { create: true, exclusive: true }, successCallback, errorCallback ); + }); + + // wait for success or error to return + waitsFor( function() { return writeComplete; }, 1000 ); + + runs(function() { + // fileEntry will be null when errorCallback is handled + expect(fileEntry).toBe(null); + + // errorCallback should be called with PATH_EXISTS_ERR + expect(error.code).toEqual(FileError.PATH_EXISTS_ERR); + }); + }); + + it("should return an error if the path is a directory", function() { + var nfs = null; + + NativeFileSystem.requestNativeFileSystem( this.path, function( fs ) { + nfs = fs; + }); + + waitsFor( function() { return nfs }, 1000); + + var fileEntry = null; + var writeComplete = false; + var error = null; + + // try to write to a path that is a directory instead of a file + runs(function() { + var successCallback = function( entry ) { + fileEntry = entry; + writeComplete = true; + } + var errorCallback = function( err ) { + error = err; + writeComplete = true; + }; + + // FIXME (jasonsj): NativeFileSystem.root is missing + nfs.getFile("dir1", { create: false }, successCallback, errorCallback ); + }); + + // wait for success or error to return + waitsFor( function() { return writeComplete; }, 1000 ); + + runs(function() { + // fileEntry will be null when errorCallback is handled + expect(fileEntry).toBe(null); + + // errorCallback should be called with TYPE_MISMATCH_ERR + expect(error.code).toEqual(FileError.TYPE_MISMATCH_ERR); + }); }); it("should create overwrite files with new content", function() { @@ -274,6 +413,7 @@ describe("NativeFileSystem", function(){ var fileEntry = null; var writeComplete = false; + var error = null; runs(function() { var successCallback = function( entry ) { @@ -283,7 +423,7 @@ describe("NativeFileSystem", function(){ fileWriter.onwriteend = function( e ) { writeComplete = true; }; - fileWriter.onerror = function( e ) { + fileWriter.onerror = function( err ) { writeComplete = true; }; @@ -312,6 +452,9 @@ describe("NativeFileSystem", function(){ runs(function() { expect(actualContents).toEqual("FileWriter.write"); + + // reset file1 content + brackets.fs.writeFile( this.path + "/file1", this.file1content, "utf8" ); }); }); From b146cacc0350b3412988bbc48edeb0ea01f2094a Mon Sep 17 00:00:00 2001 From: Jason San Jose Date: Fri, 16 Dec 2011 15:48:46 -0800 Subject: [PATCH 13/13] Fix getFile() error handling logic --- src/NativeFileSystem.js | 78 ++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 32cad0377c7..801d49a1c83 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -329,38 +329,82 @@ NativeFileSystem.DirectoryEntry.prototype.createReader = function() { return dirReader; }; +/** + * Creates or looks up a file. + * http://dev.w3.org/2009/dap/file-system/pub/FileSystem/#widl-DirectoryEntry-getFile + * + * @param {string} path Either an absolute path or a relative path from this + * DirectoryEntry to the file to be looked up or created. It is an error + * to attempt to create a file whose immediate parent does not yet + * exist. + * @param {object} options + * @param {function(entry)} successCallback + * @param {function(err)} errorCallback + */ NativeFileSystem.DirectoryEntry.prototype.getFile = function( path, options, successCallback, errorCallback ) { // TODO (jasonsj): handle absolute paths var fileFullPath = this.fullPath + "/" + path; + var createFileEntry = function () { + if ( successCallback ) { + successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); + } + } + + var createFileError = function ( err ) { + if ( errorCallback ) { + errorCallback( NativeFileSystem._nativeToFileError( err ) ); + } + } + // Use stat() to check if file exists brackets.fs.stat( fileFullPath, function( err, stats ) { - if ( options.create ) { - if ( options.exclusive && ( err !== brackets.fs.ERR_NOT_FOUND ) ) { - // throw error if file already exists - errorCallback( NativeFileSystem._nativeToFileError( brackets.fs.PATH_EXISTS_ERR ) ); + if ( ( err === brackets.fs.NO_ERROR ) ) { + // NO_ERROR implies the path already exists + + // throw error if the file the path is a directory + if ( stats.isDirectory() ) { + if ( errorCallback ) { + errorCallback( new NativeFileSystem.FileError( FileError.TYPE_MISMATCH_ERR ) ); + } + return; } - if ( err === brackets.fs.ERR_NOT_FOUND ) { + + // throw error if the file exists but create is exclusive + if ( options.create && options.exclusive ) { + if ( errorCallback ) { + errorCallback( new NativeFileSystem.FileError( FileError.PATH_EXISTS_ERR ) ); + } + + return; + } + + // Create a file entry for the existing file. If create == true, + // a file entry is created without error. + createFileEntry(); + } else if ( err === brackets.fs.ERR_NOT_FOUND ) { + // ERR_NOT_FOUND implies we write a new, empty file + + // create the file + if ( options.create ) { brackets.fs.writeFile( fileFullPath, "", "utf8", function( err ) { if ( err ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); + createFileError( err ); else - successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); + createFileEntry(); }); return; } - } - else { - // file does not exist - if ( err === brackets.fs.ERR_NOT_FOUND ) - errorCallback( NativeFileSystem._nativeToFileError( err ) ); - // path is a directory and not a file - else if ( stats.isDirectory() ) - errorCallback( new NativeFileSystem.FileError( FileError.TYPE_MISMATCH_ERR ) ); - else - successCallback( new NativeFileSystem.FileEntry( fileFullPath ) ); + + // throw error if file not found and the create == false + if ( errorCallback ) { + errorCallback( new NativeFileSystem.FileError( FileError.NOT_FOUND_ERR ) ); + } + } else { + // all other brackets.fs.stat() errors + createFileError( err ); } }); };