-
-
Save kerrishotts/7147214 to your computer and use it in GitHub Desktop.
;( | |
function( Q, BaseObject, globalContext ) { | |
/** | |
* Defined by Q, actually, but defined here to make type handling nicer | |
* @typedef {{}} Promise | |
*/ | |
var DEBUG = false; | |
/** | |
* Requests a quota from the file system | |
* @method _requestQuota | |
* @private | |
* @param {*} fileSystemType PERSISTENT or TEMPORARY | |
* @param {Number} requestedDataSize The quota we're asking for | |
* @return {Promise} The promise | |
*/ | |
function _requestQuota( fileSystemType, requestedDataSize ) { | |
var deferred = Q.defer(); | |
if ( DEBUG ) { | |
console.log( [ "_requestQuota: ", fileSystemType, requestedDataSize ].join( | |
" " ) ) | |
} | |
try { | |
// attempt to ask for a quota | |
var PERSISTENT = ( typeof LocalFileSystem !== "undefined" ) ? | |
LocalFileSystem.PERSISTENT : window.PERSISTENT; | |
// Chrome has `webkitPersistentStorage` and `navigator.webkitTemporaryStorage` | |
var storageInfo = fileSystemType == PERSISTENT ? navigator.webkitPersistentStorage : | |
navigator.webkitTemporaryStorage; | |
if ( storageInfo ) { | |
// now make sure we can request a quota | |
if ( storageInfo.requestQuota ) { | |
// request the quota | |
storageInfo.requestQuota( requestedDataSize, function success( | |
grantedBytes ) { | |
if ( DEBUG ) { | |
console.log( [ "_requestQuota: quota granted: ", fileSystemType, | |
grantedBytes | |
].join( " " ) ) | |
} | |
deferred.resolve( grantedBytes ); | |
}, function failure( anError ) { | |
if ( DEBUG ) { | |
console.log( [ "_requestQuota: quota rejected: ", fileSystemType, | |
requestedDataSize, anError | |
].join( " " ) ) | |
} | |
deferred.reject( anError ); | |
} ); | |
} else { | |
// not everything supports asking for a quota -- like Cordova. | |
// Instead, let's assume we get permission | |
if ( DEBUG ) { | |
console.log( [ | |
"_requestQuota: couldn't request quota -- no requestQuota: ", | |
fileSystemType, requestedDataSize | |
].join( " " ) ) | |
} | |
deferred.resolve( requestedDataSize ); | |
} | |
} else { | |
if ( DEBUG ) { | |
console.log( [ | |
"_requestQuota: couldn't request quota -- no storageInfo: ", | |
fileSystemType, requestedDataSize | |
].join( " " ) ) | |
} | |
deferred.resolve( requestedDataSize ); | |
} | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Request a file system with the requested size (obtained first by getting a quota) | |
* @method _requestFileSystem | |
* @private | |
* @param {*} fileSystemType TEMPORARY or PERSISTENT | |
* @param {Number} requestedDataSize The quota | |
* @return {Promise} The promise | |
*/ | |
function _requestFileSystem( fileSystemType, requestedDataSize ) { | |
var deferred = Q.defer(); | |
if ( DEBUG ) { | |
console.log( [ "_requestFileSystem: ", fileSystemType, requestedDataSize ].join( | |
" " ) ) | |
} | |
try { | |
// fix issue #2 by chasen where using `webkitRequestFileSystem` was having problems | |
// on Android 4.2.2 | |
var requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; | |
requestFileSystem( fileSystemType, requestedDataSize, function success( | |
theFileSystem ) { | |
if ( DEBUG ) { | |
console.log( [ "_requestFileSystem: got a file system", theFileSystem ] | |
.join( " " ) ) | |
} | |
deferred.resolve( theFileSystem ); | |
}, function failure( anError ) { | |
if ( DEBUG ) { | |
console.log( [ "_requestFileSystem: couldn't get a file system", | |
fileSystemType | |
].join( " " ) ) | |
} | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Resolves theURI to a fileEntry or directoryEntry, if possible. | |
* If `theURL` contains `private` or `localhost` as its first element, it will be removed. If | |
* `theURL` does not have a URL scheme, `file://` will be assumed. | |
* @method _resolveLocalFileSystemURL | |
* @private | |
* @param {String} theURL the path, should start with file://, but if it doesn't we'll add it. | |
*/ | |
function _resolveLocalFileSystemURL( theURL ) { | |
var deferred = Q.defer(); | |
if ( DEBUG ) { | |
console.log( [ "_resolveLocalFileSystemURL: ", theURL ].join( " " ) ) | |
} | |
try { | |
// split the parts of the URL | |
var parts = theURL.split( ":" ); | |
var protocol, path; | |
// can only have two parts | |
if ( parts.length > 2 ) { | |
throw new Error( "The URI is not well-formed; missing protocol: " + | |
theURL ); | |
} | |
// if only one part, we assume `file` as the protocol | |
if ( parts.length < 2 ) { | |
protocol = "file"; | |
path = parts[ 0 ]; | |
} else { | |
protocol = parts[ 0 ]; | |
path = parts[ 1 ]; | |
} | |
// split the path components | |
var pathComponents = path.split( "/" ); | |
var newPathComponents = []; | |
// iterate over each component and trim as we go | |
pathComponents.forEach( function( part ) { | |
part = part.trim(); | |
if ( part !== "" ) { // remove /private if it is the first item in the new array, for iOS sake | |
if ( !( ( part === "private" || part === "localhost" ) && | |
newPathComponents.length === 0 ) ) { | |
newPathComponents.push( part ); | |
} | |
} | |
} ); | |
// rejoin the path components | |
var theNewURI = newPathComponents.join( "/" ); | |
// add the protocol | |
theNewURI = protocol + ":///" + theNewURI; | |
// and resolve the URL. | |
window.resolveLocalFileSystemURL( theNewURI, function( theEntry ) { | |
deferred.resolve( theEntry ); | |
}, function( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* @typedef {{}} DirectoryEntry | |
* HTML5 File API Directory Type | |
*/ | |
/** | |
* Returns a directory entry based on the path from the parent using | |
* the specified options, if specified. `options` takes the form: | |
* ` {create: true/false, exclusive true/false }` | |
* @method _getDirectoryEntry | |
* @private | |
* @param {DirectoryEntry} parent The parent that path is relative from (or absolute) | |
* @param {String} path The relative or absolute path or a {DirectoryEntry} | |
* @param {Object} options The options (that is, create the directory if it doesn't exist, etc.) | |
* @return {Promise} The promise | |
*/ | |
function _getDirectoryEntry( parent, path, options ) { | |
if ( DEBUG ) { | |
console.log( [ "_getDirectoryEntry:", parent, path, options ].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
if ( typeof path === "object" ) { | |
deferred.resolve( path ); | |
} else { | |
parent.getDirectory( path, options || {}, function success( | |
theDirectoryEntry ) { | |
deferred.resolve( theDirectoryEntry ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Returns a file entry based on the path from the parent using | |
* the specified options. `options` takes the form of `{ create: true/false, exclusive: true/false}` | |
* @method getFileEntry | |
* @private | |
* @param {DirectoryEntry} parent The parent that path is relative from (or absolute) | |
* @param {String} path The relative or absolute path | |
* @param {Object} options The options (that is, create the file if it doesn't exist, etc.) | |
* @return {Promise} The promise | |
*/ | |
function _getFileEntry( parent, path, options ) { | |
if ( DEBUG ) { | |
console.log( [ "_getFileEntry:", parent, path, options ].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
if ( typeof path === "object" ) { | |
deferred.resolve( path ); | |
} else { | |
parent.getFile( path, options || {}, function success( theFileEntry ) { | |
deferred.resolve( theFileEntry ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* @typedef {{}} FileEntry | |
* HTML5 File API File Entry | |
*/ | |
/** | |
* Returns a file object based on the file entry. | |
* @method _getFileObject | |
* @private | |
* @param {FileEntry} fileEntry The file Entry | |
* @return {Promise} The Promise | |
*/ | |
function _getFileObject( fileEntry ) { | |
if ( DEBUG ) { | |
console.log( [ "_getFileObject:", fileEntry ].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
fileEntry.file( function success( theFile ) { | |
deferred.resolve( theFile ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Reads the file contents from a file object. readAsKind indicates how | |
* to read the file ("Text", "DataURL", "BinaryString", "ArrayBuffer"). | |
* @method _readFileContents | |
* @private | |
* @param {File} fileObject File to read | |
* @param {String} readAsKind "Text", "DataURL", "BinaryString", "ArrayBuffer" | |
* @return {Promise} The Promise | |
*/ | |
function _readFileContents( fileObject, readAsKind ) { | |
if ( DEBUG ) { | |
console.log( [ "_readFileContents:", fileObject, readAsKind ].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
var fileReader = new FileReader(); | |
fileReader.onloadend = function( e ) { | |
deferred.resolve( e.target.result ); | |
}; | |
fileReader.onerror = function( anError ) { | |
deferred.reject( anError ); | |
}; | |
fileReader[ "readAs" + readAsKind ]( fileObject ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Creates a file writer for the file entry; `fileEntry` must exist | |
* @method _createFileWriter | |
* @private | |
* @param {FileEntry} fileEntry The file entry to write to | |
* @return {Promise} the Promise | |
*/ | |
function _createFileWriter( fileEntry ) { | |
if ( DEBUG ) { | |
console.log( [ "_createFileWriter:", fileEntry ].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
var fileWriter = fileEntry.createWriter( function success( theFileWriter ) { | |
deferred.resolve( theFileWriter ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* @typedef {{}} FileWriter | |
* HTML5 File API File Writer Type | |
*/ | |
/** | |
* Write the contents to the fileWriter; `contents` should be a Blob. | |
* @method _writeFileContents | |
* @private | |
* @param {FileWriter} fileWriter Obtained from _createFileWriter | |
* @param {*} contents The contents to write | |
* @return {Promise} the Promise | |
*/ | |
function _writeFileContents( fileWriter, contents ) { | |
if ( DEBUG ) { | |
console.log( [ "_writeFileContents:", fileWriter, contents ].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
fileWriter.onwrite = function( e ) { | |
fileWriter.onwrite = function( e ) { | |
deferred.resolve( e ); | |
}; | |
fileWriter.write( contents ); | |
}; | |
fileWriter.onError = function( anError ) { | |
deferred.reject( anError ); | |
}; | |
fileWriter.truncate( 0 ); // clear out the contents, first | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Copy the file to the specified parent directory, with an optional new name | |
* @method _copyFile | |
* @private | |
* @param {FileEntry} theFileEntry The file to copy | |
* @param {DirectoryEntry} theParentDirectoryEntry The parent directory to copy the file to | |
* @param {String} theNewName The new name of the file ( or undefined simply to copy ) | |
* @return {Promise} The Promise | |
*/ | |
function _copyFile( theFileEntry, theParentDirectoryEntry, theNewName ) { | |
if ( DEBUG ) { | |
console.log( [ "_copyFile:", theFileEntry, theParentDirectoryEntry, | |
theNewName | |
].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
theFileEntry.copyTo( theParentDirectoryEntry, theNewName, function success( | |
theNewFileEntry ) { | |
deferred.resolve( theNewFileEntry ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Move the file to the specified parent directory, with an optional new name | |
* @method _moveFile | |
* @private | |
* @param {FileEntry} theFileEntry The file to move or rename | |
* @param {DirectoryEntry} theParentDirectoryEntry The parent directory to move the file to (or the same as the file in order to rename) | |
* @param {String} theNewName The new name of the file ( or undefined simply to move ) | |
* @return {Promise} The Promise | |
*/ | |
function _moveFile( theFileEntry, theParentDirectoryEntry, theNewName ) { | |
if ( DEBUG ) { | |
console.log( [ "_moveFile:", theFileEntry, theParentDirectoryEntry, | |
theNewName | |
].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
theFileEntry.moveTo( theParentDirectoryEntry, theNewName, function success( | |
theNewFileEntry ) { | |
deferred.resolve( theNewFileEntry ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Remove the file from the file system | |
* @method _removeFile | |
* @private | |
* @param {FileEntry} theFileEntry The file to remove | |
* @return {Promise} The Promise | |
*/ | |
function _removeFile( theFileEntry ) { | |
if ( DEBUG ) { | |
console.log( [ "_removeFile:", theFileEntry ].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
theFileEntry.remove( function success() { | |
deferred.resolve(); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Copies a directory to the specified directory, with an optional new name. The directory | |
* is copied recursively. | |
* @method _copyDirectory | |
* @private | |
* @param {DirectoryEntry} theDirectoryEntry The directory to copy | |
* @param {DirectoryEntry} theParentDirectoryEntry The parent directory to copy the first directory to | |
* @param {String} theNewName The optional new name for the directory | |
* @return {Promise} A promise | |
*/ | |
function _copyDirectory( theDirectoryEntry, theParentDirectoryEntry, theNewName ) { | |
if ( DEBUG ) { | |
console.log( [ "_copyDirectory:", theDirectoryEntry, | |
theParentDirectoryEntry, | |
theNewName | |
].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
theDirectoryEntry.copyTo( theParentDirectoryEntry, theNewName, function success( | |
theNewDirectoryEntry ) { | |
deferred.resolve( theNewDirectoryEntry ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Moves a directory to the specified directory, with an optional new name. The directory | |
* is moved recursively. | |
* @method _moveDirectory | |
* @private | |
* @param {DirectoryEntry} theDirectoryEntry The directory to move | |
* @param {DirectoryEntry} theParentDirectoryEntry The parent directory to move the first directory to | |
* @param {String} theNewName The optional new name for the directory | |
* @return {Promise} A promise | |
*/ | |
function _moveDirectory( theDirectoryEntry, theParentDirectoryEntry, theNewName ) { | |
if ( DEBUG ) { | |
console.log( [ "_moveDirectory:", theDirectoryEntry, | |
theParentDirectoryEntry, | |
theNewName | |
].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
theDirectoryEntry.moveTo( theParentDirectoryEntry, theNewName, function success( | |
theNewDirectoryEntry ) { | |
deferred.resolve( theNewDirectoryEntry ); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Removes a directory from the file system. If recursively is true, the directory is removed | |
* recursively. | |
* @method _removeDirectory | |
* @private | |
* @param {DirectoryEntry} theDirectoryEntry The directory to remove | |
* @param {Boolean} recursively If true, remove recursively | |
* @return {Promise} The Promise | |
*/ | |
function _removeDirectory( theDirectoryEntry, recursively ) { | |
if ( DEBUG ) { | |
console.log( [ "_removeDirectory:", theDirectoryEntry, "recursively", | |
recursively | |
].join( " " ) ) | |
} | |
var deferred = Q.defer(); | |
try { | |
if ( !recursively ) { | |
theDirectoryEntry.remove( function success() { | |
deferred.resolve(); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} else { | |
theDirectoryEntry.removeRecursively( function success() { | |
deferred.resolve(); | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* Reads the contents of a directory | |
* @method _readDirectoryContents | |
* @private | |
* @param {DirectoryEntry} theDirectoryEntry The directory to list | |
* @return {Promise} The promise | |
*/ | |
function _readDirectoryContents( theDirectoryEntry ) { | |
if ( DEBUG ) { | |
console.log( [ "_readDirectoryContents:", theDirectoryEntry ].join( " " ) ) | |
} | |
function readEntries() { | |
directoryReader.readEntries( function success( theEntries ) { | |
if ( !theEntries.length ) { | |
deferred.resolve( entries ); | |
} else { | |
entries = entries.concat( Array.prototype.slice.call( theEntries || [], | |
0 ) ); | |
readEntries(); | |
} | |
}, function failure( anError ) { | |
deferred.reject( anError ); | |
} ); | |
} | |
var deferred = Q.defer(); | |
try { | |
var directoryReader = theDirectoryEntry.createReader(); | |
var entries = []; | |
readEntries(); | |
} catch ( anError ) { | |
deferred.reject( anError ); | |
} | |
return deferred.promise; | |
} | |
/** | |
* @class FileManager | |
*/ | |
var _className = "UTIL.FileManager"; | |
var FileManager = function() { | |
var self; | |
// determine if we have a `BaseObject` available or not | |
var hasBaseObject = ( typeof BaseObject !== "undefined" ); | |
if ( hasBaseObject ) { | |
// if we do, subclass it | |
self = new BaseObject(); | |
self.subclass( _className ); | |
self.registerNotification( "changedCurrentWorkingDirectory" ); | |
} else { | |
// otherwise, base off {} | |
self = {}; | |
} | |
// get the persistent and temporary filesystem constants | |
self.PERSISTENT = ( typeof LocalFileSystem !== "undefined" ) ? | |
LocalFileSystem.PERSISTENT : window.PERSISTENT; | |
self.TEMPORARY = ( typeof LocalFileSystem !== "undefined" ) ? | |
LocalFileSystem.TEMPORARY : window.TEMPORARY; | |
// determine the various file types we support | |
self.FILETYPE = { | |
TEXT: "Text", | |
DATA_URL: "DataURL", | |
BINARY: "BinaryString", | |
ARRAY_BUFFER: "ArrayBuffer" | |
}; | |
/** | |
* Returns the value of the global `DEBUG` variable. | |
* @method getGlobalDebug | |
* @returns {Boolean} | |
*/ | |
self.getGlobalDebug = function() { | |
return DEBUG; | |
}; | |
/** | |
* Sets the global DEBUG variable. If `true`, debug messages are logged to the console. | |
* @method setGlobalDebug | |
* @param {Boolean} debug | |
*/ | |
self.setGlobalDebug = function( debug ) { | |
DEBUG = debug; | |
}; | |
/** | |
* @property globalDebug | |
* @type {Boolean} If `true`, logs messages to console as operations occur. | |
*/ | |
Object.defineProperty( self, "globalDebug", { | |
get: self.getGlobalDebug, | |
set: self.setGlobalDebug, | |
configurable: true | |
} ); | |
/** | |
* the fileSystemType can either be `self.PERSISTENT` or `self.TEMPORARY`, and is only | |
* set during an `init` operation. It cannot be set at any other time. | |
* @property fileSystemType | |
* @type {FileSystem} | |
*/ | |
self._fileSystemType = null; // can only be changed during INIT | |
self.getFileSystemType = function() { | |
return self._fileSystemType; | |
}; | |
Object.defineProperty( self, "fileSystemType", { | |
get: self.getFileSystemType, | |
configurable: true | |
} ); | |
/** | |
* The requested quota -- stored for future reference, since we ask for it | |
* specifically during an `init` operation. It cannot be changed. | |
* @property requestedQuota | |
* @type {Number} | |
*/ | |
self._requestedQuota = 0; // can only be changed during INIT | |
self.getRequestedQuota = function() { | |
return self._requestedQuota; | |
}; | |
Object.defineProperty( self, "requestedQuota", { | |
get: self.getRequestedQuota, | |
configurable: true | |
} ); | |
/** | |
* The actual quota obtained from the system. It cannot be changed, and is | |
* only obtained during `init`. The result does not have to match the | |
* `requestedQuota`. If it doesn't match, it may be representative of the | |
* actual space available, depending on the platform | |
* @property actualQuota | |
* @type {Number} | |
*/ | |
self._actualQuota = 0; | |
self.getActualQuota = function() { | |
return self._actualQuota; | |
}; | |
Object.defineProperty( self, "actualQuota", { | |
get: self.getActualQuota, | |
configurable: true | |
} ); | |
/** | |
* @typedef {{}} FileSystem | |
* HTML5 File API File System | |
*/ | |
/** | |
* The current filesystem -- either the temporary or persistent one; it can't be changed | |
* @property fileSystem | |
* @type {FileSystem} | |
*/ | |
self._fileSystem = null; | |
self.getFileSystem = function() { | |
return self._fileSystem; | |
}; | |
Object.defineProperty( self, "fileSystem", { | |
get: self.getFileSystem, | |
configurable: true | |
} ); | |
/** | |
* Current Working Directory Entry | |
* @property cwd | |
* @type {DirectoryEntry} | |
*/ | |
self._root = null; | |
self._cwd = null; | |
self.getCurrentWorkingDirectory = function() { | |
return self._cwd; | |
}; | |
self.setCurrentWorkingDirectory = function( theCWD ) { | |
self._cwd = theCWD; | |
if ( hasBaseObject ) { | |
self.notify( "changedCurrentWorkingDirectory" ); | |
} | |
}; | |
Object.defineProperty( self, "cwd", { | |
get: self.getCurrentWorkingDirectory, | |
set: self.setCurrentWorkingDirectory, | |
configurable: true | |
} ); | |
Object.defineProperty( self, "currentWorkingDirectory", { | |
get: self.getCurrentWorkingDirectory, | |
set: self.setCurrentWorkingDirectory, | |
configurable: true | |
} ); | |
/** | |
* Current Working Directory stack | |
* @property _cwds | |
* @private | |
* @type {Array} | |
*/ | |
self._cwds = []; | |
/** | |
* Push the current working directory on to the stack | |
* @method pushCurrentWorkingDirectory | |
*/ | |
self.pushCurrentWorkingDirectory = function() { | |
self._cwds.push( self._cwd ); | |
}; | |
/** | |
* Pop the topmost directory on the stack and change to it | |
* @method popCurrentWorkingDirectory | |
*/ | |
self.popCurrentWorkingDirectory = function() { | |
self.setCurrentWorkingDirectory( self._cwds.pop() ); | |
}; | |
/** | |
* Resolves a URL to a local file system. If the URL scheme is not present, `file` | |
* is assumed. | |
* @param {String} theURI The URI to resolve | |
*/ | |
self.resolveLocalFileSystemURL = function( theURI ) { | |
var deferred = Q.defer(); | |
_resolveLocalFileSystemURL( theURI ).then( function gotEntry( theEntry ) { | |
deferred.resolve( theEntry ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Returns the file entry for the given path (useful for | |
* getting the full path of a file). `options` is of the | |
* form `{create: true/false, exclusive: true/false}` | |
* @method getFileEntry | |
* @param {String} theFilePath The file path or FileEntry object | |
* @param {*} options creation options | |
*/ | |
self.getFileEntry = function( theFilePath, options ) { | |
var deferred = Q.defer(); | |
_getFileEntry( self._cwd, theFilePath, options ).then( function gotFileEntry( | |
theFileEntry ) { | |
deferred.resolve( theFileEntry ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Returns the file object for a given file (useful for getting | |
* the size of a file); `option` is of the form `{create: true/false, exclusive: true/false}` | |
* @method getFile | |
* @param {String} theFilePath | |
* @param {*} option | |
*/ | |
self.getFile = function( theFilePath, options ) { | |
return self.getFileEntry( theFilePath, options ).then( _getFileObject ); | |
}; | |
/** | |
* Returns the directory entry for a given path | |
* @method getDirectoryEntry | |
* @param {String} theDirectoryPath | |
* @param {*} options | |
*/ | |
self.getDirectoryEntry = function( theDirectoryPath, options ) { | |
var deferred = Q.defer(); | |
_getDirectoryEntry( self._cwd, theDirectoryPath, options ).then( function gotDirectoryEntry( | |
theDirectoryEntry ) { | |
deferred.resolve( theDirectoryEntry ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* returns the URL for a given file | |
* @method getFileURL | |
* @param {String} theFilePath | |
* @param {*} options | |
*/ | |
self.getFileURL = function( theFilePath, options ) { | |
var deferred = Q.defer(); | |
_getFileEntry( self._cwd, theFilePath, options ).then( function gotFileEntry( | |
theFileEntry ) { | |
deferred.resolve( theFileEntry.toURL() ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Returns a URL for the given directory | |
* @method getDirectoryURL | |
* @param {String} thePath | |
* @param {*} options | |
*/ | |
self.getDirectoryURL = function( thePath, options ) { | |
var deferred = Q.defer(); | |
_getDirectoryEntry( self._cwd, thePath || ".", options ).then( function gotDirectoryEntry( | |
theDirectoryEntry ) { | |
deferred.resolve( theDirectoryEntry.toURL() ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Returns the native URL for an entry by combining the `fullPath` of the entry | |
* with the `nativeURL` of the `root` directory if absolute or of the `current` | |
* directory if not absolute. | |
* @method getNativeURL | |
* @param {String} theEntry Path of the file or directory; can also be a File/DirectoryEntry | |
*/ | |
self.getNativeURL = function( theEntry ) { | |
var thePath = theEntry; | |
if ( typeof theEntry !== "string" ) { | |
thePath = theEntry.fullPath(); | |
} | |
var isAbsolute = ( thePath.substr( 0, 1 ) === "/" ); | |
var theRootPath = isAbsolute ? self._root.nativeURL : self.cwd.nativeURL; | |
return theRootPath + ( isAbsolute ? "" : "/" ) + thePath; | |
}; | |
/** | |
* returns the native file path for a given file | |
* @method getNativeFileURL | |
* @param {String} theFilePath | |
* @param {*} options | |
*/ | |
self.getNativeFileURL = function( theFilePath, options ) { | |
var deferred = Q.defer(); | |
_getFileEntry( self._cwd, theFilePath, options ).then( function gotFileEntry( | |
theFileEntry ) { | |
deferred.resolve( theFileEntry.nativeURL ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Returns a URL for the given directory | |
* @method getNativeDirectoryURL | |
* @param {String} thePath | |
* @param {*} options | |
*/ | |
self.getNativeDirectoryURL = function( thePath, options ) { | |
var deferred = Q.defer(); | |
_getDirectoryEntry( self._cwd, thePath || ".", options ).then( function gotDirectoryEntry( | |
theDirectoryEntry ) { | |
deferred.resolve( theDirectoryEntry.nativeURL ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Change to an arbitrary directory | |
* @method changeDirectory | |
* @param {String} theNewPath The path to the directory, relative to cwd | |
* @return {Promise} The Promise | |
*/ | |
self.changeDirectory = function( theNewPath ) { | |
var deferred = Q.defer(); | |
_getDirectoryEntry( self._cwd, theNewPath, {} ).then( function gotDirectory( | |
theNewDirectory ) { | |
self.cwd = theNewDirectory; | |
} ).then( function allDone() { | |
deferred.resolve( self ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Read an arbitrary file's contents. | |
* @method readFileContents | |
* @param {String} theFilePath The path to the file, relative to cwd | |
* @param {Object} options The options to use when opening the file (such as creating it) | |
* @param {String} readAsKind How to read the file -- best to use self.FILETYPE.TEXT, etc. | |
* @return {Promise} The Promise | |
*/ | |
self.readFileContents = function( theFilePath, options, readAsKind ) { | |
var deferred = Q.defer(); | |
_getFileEntry( self._cwd, theFilePath, options || {} ).then( function gotTheFileEntry( | |
theFileEntry ) { | |
return _getFileObject( theFileEntry ); | |
} ).then( function gotTheFileObject( theFileObject ) { | |
return _readFileContents( theFileObject, readAsKind || "Text" ); | |
} ).then( function getTheFileContents( theFileContents ) { | |
deferred.resolve( theFileContents ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Read an arbitrary directory's entries. | |
* @method readDirectoryContents | |
* @param {String} theDirectoryPath The path to the directory, relative to cwd; "." if not specified | |
* @param {Object} options The options to use when opening the directory (such as creating it) | |
* @return {Promise} The Promise | |
*/ | |
self.readDirectoryContents = function( theDirectoryPath, options ) { | |
var deferred = Q.defer(); | |
_getDirectoryEntry( self._cwd, theDirectoryPath || ".", options || {} ).then( | |
function gotTheDirectoryEntry( theDirectoryEntry ) { | |
return _readDirectoryContents( theDirectoryEntry ); | |
} ).then( function gotTheDirectoryEntries( theEntries ) { | |
deferred.resolve( theEntries ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Write data to an arbitrary file | |
* @method writeFileContents | |
* @param {String} theFilePath The file name to write to, relative to cwd | |
* @param {Object} options The options to use when opening the file | |
* @param {*} theData The data to write | |
* @return {Promise} The Promise | |
*/ | |
self.writeFileContents = function( theFilePath, options, theData ) { | |
var deferred = Q.defer(); | |
_getFileEntry( self._cwd, theFilePath, options || { | |
create: true, | |
exclusive: false | |
} ).then( function gotTheFileEntry( theFileEntry ) { | |
return _createFileWriter( theFileEntry ); | |
} ).then( function gotTheFileWriter( theFileWriter ) { | |
return _writeFileContents( theFileWriter, theData ); | |
} ).then( function allDone() { | |
deferred.resolve( self ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Creates an arbitrary directory | |
* @method createDirectory | |
* @param {String} theDirectoryPath The path, relative to cwd | |
* @return {Promise} The Promise | |
*/ | |
self.createDirectory = function( theDirectoryPath ) { | |
var deferred = Q.defer(); | |
_getDirectoryEntry( self._cwd, theDirectoryPath, { | |
create: true, | |
exclusive: false | |
} ).then( function gotDirectory( theNewDirectory ) { | |
deferred.resolve( theNewDirectory ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Copies a file to a new directory, with an optional new name | |
* @method copyFile | |
* @param {String} sourceFilePath Path to file, relative to cwd | |
* @param {String} targetDirectoryPath Path to new directory, relative to cwd | |
* @param {String} withNewName New name, if desired | |
* @return {Promise} The Promise | |
*/ | |
self.copyFile = function( sourceFilePath, targetDirectoryPath, withNewName ) { | |
var deferred = Q.defer(); | |
var theFileToCopy; | |
_getFileEntry( self._cwd, sourceFilePath, {} ).then( function gotFileEntry( | |
aFileToCopy ) { | |
theFileToCopy = aFileToCopy; | |
return _getDirectoryEntry( self._cwd, targetDirectoryPath, {} ); | |
} ).then( function gotDirectoryEntry( theTargetDirectory ) { | |
return _copyFile( theFileToCopy, theTargetDirectory, withNewName ); | |
} ).then( function allDone( theNewFileEntry ) { | |
deferred.resolve( theNewFileEntry ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Copies a directory to a new directory, with an optional new name | |
* @method copyDirectory | |
* @param {String} sourceDirectoryPath Path to directory, relative to cwd | |
* @param {String} targetDirectoryPath Path to new directory, relative to cwd | |
* @param {String} withNewName New name, if desired | |
* @return {Promise} The Promise | |
*/ | |
self.copyDirectory = function( sourceDirectoryPath, targetDirectoryPath, | |
withNewName ) { | |
var deferred = Q.defer(); | |
var theDirectoryToCopy; | |
_getDirectoryEntry( self._cwd, sourceDirectoryPath, {} ).then( function gotSourceDirectoryEntry( | |
sourceDirectoryEntry ) { | |
theDirectoryToCopy = sourceDirectoryEntry; | |
return _getDirectoryEntry( self._cwd, targetDirectoryPath, {} ); | |
} ).then( function gotTargetDirectoryEntry( theTargetDirectory ) { | |
return _copyDirectory( theDirectoryToCopy, theTargetDirectory, | |
withNewName ); | |
} ).then( function allDone( theNewDirectoryEntry ) { | |
deferred.resolve( theNewDirectoryEntry ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* @method moveFile | |
* Moves a file to a new directory, with an optional new name | |
* @param {String} sourceFilePath Path to file, relative to cwd | |
* @param {String} targetDirectoryPath Path to new directory, relative to cwd | |
* @param {String} withNewName New name, if desired | |
* @return {Promise} The Promise | |
*/ | |
self.moveFile = function( sourceFilePath, targetDirectoryPath, withNewName ) { | |
var deferred = Q.defer(); | |
var theFileToMove; | |
_getFileEntry( self._cwd, sourceFilePath, {} ).then( function gotFileEntry( | |
aFileToMove ) { | |
theFileToMove = aFileToMove; | |
return _getDirectoryEntry( self._cwd, targetDirectoryPath, {} ); | |
} ).then( function gotDirectoryEntry( theTargetDirectory ) { | |
return _moveFile( theFileToMove, theTargetDirectory, withNewName ); | |
} ).then( function allDone( theNewFileEntry ) { | |
deferred.resolve( theNewFileEntry ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Moves a directory to a new directory, with an optional new name | |
* @method moveDirectory | |
* @param {String} sourceDirectoryPath Path to directory, relative to cwd | |
* @param {String} targetDirectoryPath Path to new directory, relative to cwd | |
* @param {String} withNewName New name, if desired | |
* @return {Promise} The Promise | |
*/ | |
self.moveDirectory = function( sourceDirectoryPath, targetDirectoryPath, | |
withNewName ) { | |
var deferred = Q.defer(); | |
var theDirectoryToMove; | |
_getDirectoryEntry( self._cwd, sourceDirectoryPath, {} ).then( function gotSourceDirectoryEntry( | |
sourceDirectoryEntry ) { | |
theDirectoryToMove = sourceDirectoryEntry; | |
return _getDirectoryEntry( self._cwd, targetDirectoryPath, {} ); | |
} ).then( function gotTargetDirectoryEntry( theTargetDirectory ) { | |
return _moveDirectory( theDirectoryToMove, theTargetDirectory, | |
withNewName ); | |
} ).then( function allDone( theNewDirectoryEntry ) { | |
deferred.resolve( theNewDirectoryEntry ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Renames a file to a new name, in the cwd | |
* @method renameFile | |
* @param {String} sourceFilePath Path to file, relative to cwd | |
* @param {String} withNewName New name | |
* @return {Promise} The Promise | |
*/ | |
self.renameFile = function( sourceFilePath, withNewName ) { | |
return self.moveFile( sourceFilePath, ".", withNewName ); | |
}; | |
/** | |
* Renames a directory to a new name, in the cwd | |
* @method renameDirectory | |
* @param {String} sourceDirectoryPath Path to directory, relative to cwd | |
* @param {String} withNewName New name | |
* @return {Promise} The Promise | |
*/ | |
self.renameDirectory = function( sourceDirectoryPath, withNewName ) { | |
return self.moveDirectory( sourceDirectoryPath, ".", withNewName ); | |
}; | |
/** | |
* Deletes a file | |
* @method deleteFile | |
* @param {String} theFilePath Path to file, relative to cwd | |
* @return {Promise} The Promise | |
*/ | |
self.deleteFile = function( theFilePath ) { | |
var deferred = Q.defer(); | |
_getFileEntry( self._cwd, theFilePath, {} ).then( function gotTheFileToDelete( | |
theFileEntry ) { | |
return _removeFile( theFileEntry ); | |
} ).then( function allDone() { | |
deferred.resolve( self ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Removes a directory, possibly recursively | |
* @method removeDirectory | |
* @param {String} theDirectoryPath path to directory, relative to cwd | |
* @param {Boolean} recursively If true, recursive remove | |
* @return {Promise} The promise | |
*/ | |
self.removeDirectory = function( theDirectoryPath, recursively ) { | |
var deferred = Q.defer(); | |
_getDirectoryEntry( self._cwd, theDirectoryPath, {} ).then( function gotTheDirectoryToDelete( | |
theDirectoryEntry ) { | |
return _removeDirectory( theDirectoryEntry, recursively ); | |
} ).then( function allDone() { | |
deferred.resolve( self ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
/** | |
* Asks the browser for the requested quota, and then requests the file system | |
* and sets the cwd to the root directory. | |
* @method _initializeFileSystem | |
* @private | |
* @return {Promise} The promise | |
*/ | |
self._initializeFileSystem = function() { | |
var deferred = Q.defer(); | |
_requestQuota( self.fileSystemType, self.requestedQuota ).then( function gotQuota( | |
theQuota ) { | |
self._actualQuota = theQuota; | |
return _requestFileSystem( self.fileSystemType, self.actualQuota ); | |
} ).then( function gotFS( theFS ) { | |
self._fileSystem = theFS; | |
//self._cwd = theFS.root; | |
return _getDirectoryEntry( theFS.root, "", {} ); | |
} ).then( function gotRootDirectory( theRootDirectory ) { | |
self._root = theRootDirectory; | |
self._cwd = theRootDirectory; | |
} ).then( function allDone() { | |
deferred.resolve( self ); | |
} ).catch( function( anError ) { | |
deferred.reject( anError ); | |
} ).done(); | |
return deferred.promise; | |
}; | |
if ( self.overrideSuper ) { | |
self.overrideSuper( self.class, "init", self.init ); | |
} | |
/** | |
* Initializes the file manager with the requested file system type (self.PERSISTENT or self.TEMPORARY) | |
* and requested quota size. Both must be specified. | |
* @method init | |
* @param {FileSystem} fileSystemType | |
* @param {Number} requestedQuota | |
*/ | |
self.init = function( fileSystemType, requestedQuota ) { | |
if ( self.super ) { | |
self.super( _className, "init" ); | |
} | |
if ( typeof fileSystemType === "undefined" ) { | |
throw new Error( | |
"No file system type specified; specify PERSISTENT or TEMPORARY." ); | |
} | |
if ( typeof requestedQuota === "undefined" ) { | |
throw new Error( "No quota requested. If you don't know, specify ZERO." ); | |
} | |
self._requestedQuota = requestedQuota; | |
self._fileSystemType = fileSystemType; | |
return self._initializeFileSystem(); // this returns a promise, so we can .then after. | |
}; | |
/** | |
* Initializes the file manager with the requested file system type (self.PERSISTENT or self.TEMPORARY) | |
* and requested quota size. Both must be specified. | |
* @method initWithOptions | |
* @param {*} options | |
*/ | |
self.initWithOptions = function( options ) { | |
if ( typeof options === "undefined" ) { | |
throw new Error( "No options specified. Need type and quota." ); | |
} | |
if ( typeof options.fileSystemType === "undefined" ) { | |
throw new Error( | |
"No file system type specified; specify PERSISTENT or TEMPORARY." ); | |
} | |
if ( typeof options.requestedQuota === "undefined" ) { | |
throw new Error( "No quota requested. If you don't know, specify ZERO." ); | |
} | |
return self.init( options.fileSystemType, options.requestedQuota ); | |
}; | |
return self; | |
}; | |
// meta information | |
FileManager.meta = { | |
version: '00.04.450', | |
class: _className, | |
autoInitializable: false, | |
categorizable: false | |
}; | |
// assign to `window` if stand-alone | |
if ( globalContext ) { | |
globalContext.FileManager = FileManager | |
} | |
// return factory | |
return FileManager; | |
} )( Q, undefined, ( typeof IN_YASMF !== "undefined" ) ? undefined : window ); |
Updated to most recent version of FileManager. Example updated as well.
Updated to most recent version of FileManager in order to fix some issues discovered by @chasen. Example jsbin also updated.
What is the status of FileManager now (12/2/16)? Does it still require Q.js? Are there any examples of usage? The referenced jsbin seems to be empty.
Hi Kerry, I'm Rei from Rome, Italy. I have a proprietary file manager that gives me a lot of problems with callbacks, I found your wandering around the web and it seems pretty interesting, I would like to replace it with mine. I wanted to ask you: does your support also the latest versions of Cordova? I'm using 6.3.0 for compatibility gestures with some plugins, but I would like to move on to newer versions, like 8. Thanks, regards. Rei
For more docs, see https://github.com/photokandyStudios/YASMF-Next/wiki/FileManager