diff --git a/src/NativeFileSystem.js b/src/NativeFileSystem.js index 1efedc348e4..350cc8ffca8 100644 --- a/src/NativeFileSystem.js +++ b/src/NativeFileSystem.js @@ -11,25 +11,30 @@ window.NativeFileSystem = { * @param {function} resultCallback * @constructor */ - showOpenDialog: function ( allowMultipleSelection, + showOpenDialog: function ( allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, - resultCallback ) { - - if( !resultCallback ) - return null; + successCallback, + errorCallback ) { + if( !successCallback ) + return; - var files = brackets.file.showOpenDialog( allowMultipleSelection, - chooseDirectories, - title, - initialPath, - fileTypes, - resultCallback ); + var files = brackets.fs.showOpenDialog( allowMultipleSelection, + chooseDirectories, + title, + initialPath, + fileTypes, + function( err, data ){ + if( ! err ) + successCallback( data ); + else if (errorCallback) + errorCallback(NativeFileSystem._nativeToFileError(err)); + }); + }, - /** requestNativeFileSystem * * @param {string} path @@ -37,22 +42,55 @@ window.NativeFileSystem = { * @param {function} errorCallback */ requestNativeFileSystem: function( path, successCallback, errorCallback ){ - - // TODO: assumes path is a directory right now. Need to error check - // TODO: don't actually need to get the listing here, but should verify the directory exists - var entryList = brackets.file.getDirectoryListing(path); - if (entryList) { - var files = JSON.parse(entryList); - var root = new DirectoryEntry( path ); - return root; - } - else { - return null; + brackets.fs.stat(path, function( err, data ){ + if( !err ){ + var root = new DirectoryEntry( path ); + successCallback( root ); + } + 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. + case brackets.fs.ERR_UNKNOWN: + 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. + case brackets.fs.ERR_UNSUPPORTED_ENCODING: + error = FileError.SECURITY_ERR; + break; + + case brackets.fs.ERR_CANT_WRITE: + error = FileError.NO_MODIFICATION_ALLOWED_ERR; + break; + case brackets.fs.ERR_OUT_OF_SPACE: + error = FileError.QUOTA_EXCEEDED_ERR; + break; } - } + + return new FileError(error); + } }; - /** class: Entry * * @param {string} name @@ -73,7 +111,7 @@ Entry = function( fullPath, isDirectory) { this.name = pathParts.pop(); } - // IMPLEMENT LATERvar filesystem; + // 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); // IMPLEMENT LATER DOMString toURL (optional DOMString mimeType); @@ -131,7 +169,7 @@ DirectoryReader = function() { }; -/** readEntires +/** readEntries * * @param {function} successCallback * @param {function} errorCallback @@ -139,31 +177,63 @@ DirectoryReader = function() { */ DirectoryReader.prototype.readEntries = function( successCallback, errorCallback ){ var rootPath = this._directory.fullPath; - var jsonList = brackets.file.getDirectoryListing( rootPath ); - var nameList = JSON.parse(jsonList); - - // Create entries for each name - var entries = []; - nameList.forEach(function(item){ - // Ignore names starting with "." - if (item.indexOf(".") != 0) { - var itemFullPath = rootPath + "/" + item; - - if( brackets.file.isDirectory( itemFullPath ) ) { - entries.push( new DirectoryEntry( itemFullPath ) ); - } - else { - entries.push( new FileEntry( itemFullPath ) ); - } + var jsonList = brackets.fs.readdir( rootPath, function( err, filelist ) { + if( ! err ){ + // Create entries for each name + 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 ) ) + entries.push( new FileEntry( itemFullPath ) ); + } + else if (errorCallback) { + errorCallback(NativeFileSystem._nativeToFileError(err)); + } + + }) + }); + + successCallback( entries ); } - }); - - - - successCallback( entries ); - - // TODO: error handling + else if (errorCallback) { + errorCallback(NativeFileSystem._nativeToFileError(err)); + } + }); }; +/** class: FileError + * + * 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 + * comprehensive. + * + * @constructor + * @param {number} code The error code to return with this FileError. Must be + * one of the codes defined in the FileError class. + */ +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 +}); diff --git a/src/brackets.js b/src/brackets.js index a2c9d89ab63..d2c617ec431 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -13,10 +13,50 @@ $(document).ready(function() { // Temporary button to test file directory traversa; $("#menu-file-open").click(function(){ if (!inBrowser) { - window.NativeFileSystem.showOpenDialog(false, true, "Choose a folder", null, null, showOpenDialogCallback); + window.NativeFileSystem.showOpenDialog( false, true, "Choose a folder", + null, null, + showOpenDialogSuccessCallback, + showOpenDialogErrorCallback); + + /* + // TEST CODE + var reader = new FileReader(); + reader.onerror = errorHandler; + + reader.onabort = function(e) { + alert('File read cancelled'); + }; + + reader.onloadstart = function(e) { + console.log( "loading" ); + }; + + + // Read in the image file as a binary string. + reader.readAsText(file); + + + function errorHandler(evt) { + switch(evt.target.error.code) { + case evt.target.error.NOT_FOUND_ERR: + alert('File Not Found!'); + break; + case evt.target.error.NOT_READABLE_ERR: + alert('File is not readable'); + break; + case evt.target.error.ABORT_ERR: + break; // noop + default: + alert('An error occurred reading this file.'); + }; + } + +*/ + } }); + // Implements the 'Run Tests' menu to bring up the Jasmine unit test window var testWindow = null; $("#menu-runtests").click(function(){ @@ -34,21 +74,36 @@ $(document).ready(function() { } }); - function showOpenDialogCallback( files ) { + function showOpenDialogErrorCallback( err ){ + console.log( err ) + } + + function showOpenDialogSuccessCallback( files ) { var folderName = files instanceof Array ? files[0] : files; + var nestingLevel = 0; if (folderName != "") { - var rootEntry = window.NativeFileSystem.requestNativeFileSystem( folderName, null, null ); // TODO: add callbacks + window.NativeFileSystem.requestNativeFileSystem( folderName, + requestNativeFileSystemSuccessCB, requestNativeFileSystemErrorCB ); // TODO: add callbacks + + } + + function requestNativeFileSystemSuccessCB( rootEntry ){ var nestingLevel = 0; if( rootEntry && rootEntry.isDirectory ) readDirectory( rootEntry ); } + function requestNativeFileSystemErrorCB( err){ + console.log( err ); + } + // Test directory traversal function readDirectory( entry ){ + var reader = entry.createReader(); reader.readEntries( dirReaderSuccessCB, dirReaderErrorCB); } diff --git a/test/SpecRunner.html b/test/SpecRunner.html index 1e383df4d8b..ab4088e5191 100644 --- a/test/SpecRunner.html +++ b/test/SpecRunner.html @@ -24,7 +24,7 @@ - + diff --git a/test/spec/SampleTest.js b/test/spec/Editor-test.js similarity index 55% rename from test/spec/SampleTest.js rename to test/spec/Editor-test.js index 56d8db8fb8b..ebd266c0c52 100644 --- a/test/spec/SampleTest.js +++ b/test/spec/Editor-test.js @@ -1,15 +1,5 @@ -describe("Brackets", function(){ - var content = 'Brackets is going to be awesome!"\n'; - - it("should be awesome", function() { - this.addMatchers({ - toBeAwesome: function(expected) { - return true; - } - }); - - expect("Brackets").toBeAwesome(); - }); +describe("Editor", function(){ + var content = 'Brackets is going to be awesome!\n'; describe("CodeMirror", function() { var myCodeMirror; @@ -33,9 +23,3 @@ describe("Brackets", function(){ }); }); }); - -describe("Failure", function() { - it("is expected sometimes", function() { - expect("failure").toEqual("sometimes"); - }); -}); \ No newline at end of file diff --git a/test/spec/NativeFileSystem-test.js b/test/spec/NativeFileSystem-test.js index 98e10b69156..c74bc1bc6c8 100644 --- a/test/spec/NativeFileSystem-test.js +++ b/test/spec/NativeFileSystem-test.js @@ -21,9 +21,6 @@ describe("NativeFileSystem", function(){ return false; } }); - }); - - it("should read a directory from disk", function() { //TODO: Make this relative -- right now, asking for "." gives "/" //Want to be able to simply use: @@ -32,26 +29,80 @@ describe("NativeFileSystem", function(){ path = path.substr("file://".length); path = path.substr(0,path.lastIndexOf("/")+1); path = path + "spec/NativeFileSystem-test-files"; + this.path = path; + }); + it("should read a directory from disk", function() { var entries = null; var readComplete = false; - var nfs = window.NativeFileSystem.requestNativeFileSystem(path); - 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"); - }); + 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"); + }); + } + }); + + 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 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 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); + }); }); }); });