Modules (188)

AppshellFileSystem

Description

Dependencies

Variables

FILE_WATCHER_BATCH_TIMEOUT Constant

    var FILE_WATCHER_BATCH_TIMEOUT = 200;   // 200ms - granularity of file watcher changes
Private

_changeCallback

Callback to notify FileSystem of watcher changes

Type
?function(string, FileSystemStats=)
    var _changeCallback;
Private

_changeTimeout

Timeout used to batch up file watcher changes (setTimeout() return value)

    var _changeTimeout;
Private

_offlineCallback

Callback to notify FileSystem if watchers stop working entirely

Type
?function()
    var _offlineCallback;
Private

_pendingChanges

Pending file watcher changes - map from fullPath to flag indicating whether we need to pass stats to _changeCallback() for this path.

Type
!Object.<string, boolean>
    var _pendingChanges = {};

    var _bracketsPath   = FileUtils.getNativeBracketsDirectoryPath(),
        _modulePath     = FileUtils.getNativeModuleDirectoryPath(module),
        _nodePath       = "node/FileWatcherDomain",
        _domainPath     = [_bracketsPath, _modulePath, _nodePath].join("/"),
        _nodeDomain     = new NodeDomain("fileWatcher", _domainPath);

    var _isRunningOnWindowsXP = window.navigator.userAgent.indexOf("Windows NT 5.") >= 0;


    // If the connection closes, notify the FileSystem that watchers have gone offline.
    _nodeDomain.connection.on("close", function (event, promise) {
        if (_offlineCallback) {
            _offlineCallback();
        }
    });

Functions

Private

_enqueueChange

Enqueue a file change event for eventual reporting back to the FileSystem.

changedPath string
The path that was changed
stats object
Stats coming from the underlying watcher, if available
    function _enqueueChange(changedPath, stats) {
        _pendingChanges[changedPath] = stats;
        if (!_changeTimeout) {
            _changeTimeout = window.setTimeout(function () {
                if (_changeCallback) {
                    Object.keys(_pendingChanges).forEach(function (path) {
                        _changeCallback(path, _pendingChanges[path]);
                    });
                }

                _changeTimeout = null;
                _pendingChanges = {};
            }, FILE_WATCHER_BATCH_TIMEOUT);
        }
    }
Private

_fileWatcherChange

Event handler for the Node fileWatcher domain's change event.

The jQuery.Event
underlying change event
event string
The type of the event: "changed", "created" or "deleted"
parentDirPath string
The path to the directory holding entry that has changed
entryName optional string
The name of the file/directory that has changed
statsObj object
Object that can be used to construct FileSystemStats
    function _fileWatcherChange(evt, event, parentDirPath, entryName, statsObj) {
        var change;
        switch (event) {
        case "changed":
            // an existing file/directory was modified; stats are passed if available
            var fsStats;
            if (statsObj) {
                fsStats = new FileSystemStats(statsObj);
            } else {
                console.warn("FileWatcherDomain was expected to deliver stats for changed event!");
            }
            _enqueueChange(parentDirPath + entryName, fsStats);
            break;
        case "created":
        case "deleted":
            // file/directory was created/deleted; fire change on parent to reload contents
            _enqueueChange(parentDirPath, null);
            break;
        default:
            console.error("Unexpected 'change' event:", event);
        }
    }

    // Setup the change handler. This only needs to happen once.
    _nodeDomain.on("change", _fileWatcherChange);
Private

_mapError

Convert appshell error codes to FileSystemError values.

err nullable number
An appshell error code
Returns: ?string
A FileSystemError string, or null if there was no error code.
    function _mapError(err) {
        if (!err) {
            return null;
        }

        switch (err) {
        case appshell.fs.ERR_INVALID_PARAMS:
            return FileSystemError.INVALID_PARAMS;
        case appshell.fs.ERR_NOT_FOUND:
            return FileSystemError.NOT_FOUND;
        case appshell.fs.ERR_CANT_READ:
            return FileSystemError.NOT_READABLE;
        case appshell.fs.ERR_CANT_WRITE:
            return FileSystemError.NOT_WRITABLE;
        case appshell.fs.ERR_UNSUPPORTED_ENCODING:
            return FileSystemError.UNSUPPORTED_ENCODING;
        case appshell.fs.ERR_OUT_OF_SPACE:
            return FileSystemError.OUT_OF_SPACE;
        case appshell.fs.ERR_FILE_EXISTS:
            return FileSystemError.ALREADY_EXISTS;
        case appshell.fs.ERR_ENCODE_FILE_FAILED:
            return FileSystemError.ENCODE_FILE_FAILED;
        case appshell.fs.ERR_DECODE_FILE_FAILED:
            return FileSystemError.DECODE_FILE_FAILED;
        case appshell.fs.ERR_UNSUPPORTED_UTF16_ENCODING:
            return FileSystemError.UNSUPPORTED_UTF16_ENCODING;
        }
        return FileSystemError.UNKNOWN;
    }
Private

_wrap

Convert a callback to one that transforms its first parameter from an appshell error code to a FileSystemError string.

cb function(?number)
A callback that expects an appshell error code
Returns: function(?string)
A callback that expects a FileSystemError string
    function _wrap(cb) {
        return function (err) {
            var args = Array.prototype.slice.call(arguments);
            args[0] = _mapError(args[0]);
            cb.apply(null, args);
        };
    }
Public API

exists

Determine whether a file or directory exists at the given path by calling back asynchronously with either a FileSystemError string or a boolean, which is true if the file exists and false otherwise. The error will never be FileSystemError.NOT_FOUND; in that case, there will be no error and the boolean parameter will be false.

path string
callback function(?string,boolean)
    function exists(path, callback) {
        stat(path, function (err) {
            if (err) {
                if (err === FileSystemError.NOT_FOUND) {
                    callback(null, false);
                } else {
                    callback(err);
                }
                return;
            }

            callback(null, true);
        });
    }
Public API

initWatchers

Initialize file watching for this filesystem, using the supplied changeCallback to provide change notifications. The first parameter of changeCallback specifies the changed path (either a file or a directory); if this parameter is null, it indicates that the implementation cannot specify a particular changed path, and so the callers should consider all paths to have changed and to update their state accordingly. The second parameter to changeCallback is an optional FileSystemStats object that may be provided in case the changed path already exists and stats are readily available. The offlineCallback will be called in case watchers are no longer expected to function properly. All watched paths are cleared when the offlineCallback is called.

changeCallback function(?string,FileSystemStats=)
offlineCallback optional function()
    function initWatchers(changeCallback, offlineCallback) {
        _changeCallback = changeCallback;
        _offlineCallback = offlineCallback;

        if (_isRunningOnWindowsXP && _offlineCallback) {
            _offlineCallback();
        }
    }
Public API

mkdir

Create a directory at the given path, and call back asynchronously with either a FileSystemError string or a stats object for the newly created directory. The octal mode parameter is optional; if unspecified, the mode of the created directory is implementation dependent.

path string
mode optional number
The base-eight mode of the newly created directory.
callback optional function(?string, FileSystemStats=)
    function mkdir(path, mode, callback) {
        if (typeof mode === "function") {
            callback = mode;
            mode = parseInt("0755", 8);
        }
        appshell.fs.makedir(path, mode, function (err) {
            if (err) {
                callback(_mapError(err));
            } else {
                stat(path, function (err, stat) {
                    callback(err, stat);
                });
            }
        });
    }
Public API

moveToTrash

Move the file or directory at the given path to a system dependent trash location, calling back asynchronously with a possibly null FileSystemError string. Directories will be moved even when non-empty.

path string
callback optional function(string)
    function moveToTrash(path, callback) {
        appshell.fs.moveToTrash(path, function (err) {
            callback(_mapError(err));
        });
    }
Public API

readFile

Read the contents of the file at the given path, calling back asynchronously with either a FileSystemError string, or with the data and the FileSystemStats object associated with the read file. The options parameter can be used to specify an encoding (default "utf8"), and also a cached stats object that the implementation is free to use in order to avoid an additional stat call.

Note: if either the read or the stat call fails then neither the read data nor stat will be passed back, and the call should be considered to have failed. If both calls fail, the error from the read call is passed back.

path string
options {encoding: string=,stat: FileSystemStats=}
callback function(?string,string=,FileSystemStats=)
    function readFile(path, options, callback) {
        var encoding = options.encoding || "utf8";

        // callback to be executed when the call to stat completes
        //  or immediately if a stat object was passed as an argument
        function doReadFile(stat) {
            if (stat.size > (FileUtils.MAX_FILE_SIZE)) {
                callback(FileSystemError.EXCEEDS_MAX_FILE_SIZE);
            } else {
                appshell.fs.readFile(path, encoding, function (_err, _data, encoding, preserveBOM) {
                    if (_err) {
                        callback(_mapError(_err));
                    } else {
                        callback(null, _data, encoding, preserveBOM, stat);
                    }
                });
            }
        }

        if (options.stat) {
            doReadFile(options.stat);
        } else {
            exports.stat(path, function (_err, _stat) {
                if (_err) {
                    callback(_err);
                } else {
                    doReadFile(_stat);
                }
            });
        }
    }
Public API

readdir

Read the contents of the directory at the given path, calling back asynchronously either with a FileSystemError string or an array of FileSystemEntry objects along with another consistent array, each index of which either contains a FileSystemStats object for the corresponding FileSystemEntry object in the second parameter or a FileSystemError string describing a stat error.

path string
callback function(?string,Array.<FileSystemEntry>=,Array.<string,FileSystemStats>=)
    function readdir(path, callback) {
        appshell.fs.readdir(path, function (err, contents) {
            if (err) {
                callback(_mapError(err));
                return;
            }

            var count = contents.length;
            if (!count) {
                callback(null, [], []);
                return;
            }

            var stats = [];
            contents.forEach(function (val, idx) {
                stat(path + "/" + val, function (err, stat) {
                    stats[idx] = err || stat;
                    count--;
                    if (count <= 0) {
                        callback(null, contents, stats);
                    }
                });
            });
        });
    }
Public API

rename

Rename the file or directory at oldPath to newPath, and call back asynchronously with a possibly null FileSystemError string.

oldPath string
newPath string
callback optional function(?string)
    function rename(oldPath, newPath, callback) {
        appshell.fs.rename(oldPath, newPath, _wrap(callback));
    }
Public API

showOpenDialog

Display an open-files dialog to the user and call back asynchronously with either a FileSystmError string or an array of path strings, which indicate the entry or entries selected.

allowMultipleSelection boolean
chooseDirectories boolean
title string
initialPath string
fileTypes optional Array.<string>
callback function(?string,Array.<string>=)
    function showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, callback) {
        appshell.fs.showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, _wrap(callback));
    }
Public API

showSaveDialog

Display a save-file dialog and call back asynchronously with either a FileSystemError string or the path to which the user has chosen to save the file. If the dialog is cancelled, the path string will be empty.

title string
initialPath string
proposedNewFilename string
callback function(?string,string=)
    function showSaveDialog(title, initialPath, proposedNewFilename, callback) {
        appshell.fs.showSaveDialog(title, initialPath, proposedNewFilename, _wrap(callback));
    }
Public API

stat

Stat the file or directory at the given path, calling back asynchronously with either a FileSystemError string or the entry's associated FileSystemStats object.

path string
callback function(?string,FileSystemStats=)
    function stat(path, callback) {
        appshell.fs.stat(path, function (err, stats) {
            if (err) {
                callback(_mapError(err));
            } else {
                var options = {
                    isFile: stats.isFile(),
                    mtime: stats.mtime,
                    size: stats.size,
                    realPath: stats.realPath,
                    hash: stats.mtime.getTime()
                };

                var fsStats = new FileSystemStats(options);

                callback(null, fsStats);
            }
        });
    }
Public API

unlink

Unlink (i.e., permanently delete) the file or directory at the given path, calling back asynchronously with a possibly null FileSystemError string. Directories will be unlinked even when non-empty.

path string
callback optional function(string)
    function unlink(path, callback) {
        appshell.fs.unlink(path, function (err) {
            callback(_mapError(err));
        });
    }
Public API

unwatchAll

Stop providing change notifications for all previously watched files and directories, optionally calling back asynchronously with a possibly null FileSystemError string when the operation is complete.

callback optional function(?string)
    function unwatchAll(callback) {
        _nodeDomain.exec("unwatchAll")
            .then(callback, callback);
    }


    // Export public API
    exports.showOpenDialog  = showOpenDialog;
    exports.showSaveDialog  = showSaveDialog;
    exports.exists          = exists;
    exports.readdir         = readdir;
    exports.mkdir           = mkdir;
    exports.rename          = rename;
    exports.stat            = stat;
    exports.readFile        = readFile;
    exports.writeFile       = writeFile;
    exports.unlink          = unlink;
    exports.moveToTrash     = moveToTrash;
    exports.initWatchers    = initWatchers;
    exports.watchPath       = watchPath;
    exports.unwatchPath     = unwatchPath;
    exports.unwatchAll      = unwatchAll;
Public API

unwatchPath

Stop providing change notifications for the file or directory at the given path, calling back asynchronously with a possibly null FileSystemError string when the operation is complete. This function needs to mirror the signature of watchPath because of FileSystem.prototype._watchOrUnwatchEntry implementation.

path string
ignored Array<string>
callback optional function(?string)
    function unwatchPath(path, ignored, callback) {
        _nodeDomain.exec("unwatchPath", path)
            .then(callback, callback);
    }
Public API

watchPath

Start providing change notifications for the file or directory at the given path, calling back asynchronously with a possibly null FileSystemError string when the initialization is complete. Notifications are provided using the changeCallback function provided by the initWatchers method. Note that change notifications are only provided recursively for directories when the recursiveWatch property of this module is true.

path string
ignored Array<string>
callback optional function(?string)
    function watchPath(path, ignored, callback) {
        if (_isRunningOnWindowsXP) {
            callback(FileSystemError.NOT_SUPPORTED);
            return;
        }
        appshell.fs.isNetworkDrive(path, function (err, isNetworkDrive) {
            if (err || isNetworkDrive) {
                if (isNetworkDrive) {
                    callback(FileSystemError.NETWORK_DRIVE_NOT_SUPPORTED);
                } else {
                    callback(FileSystemError.UNKNOWN);
                }
                return;
            }
            _nodeDomain.exec("watchPath", path, ignored)
                .then(callback, callback);
        });
    }
Public API

writeFile

Write data to the file at the given path, calling back asynchronously with either a FileSystemError string or the FileSystemStats object associated with the written file and a boolean that indicates whether the file was created by the write (true) or not (false). If no file exists at the given path, a new file will be created. The options parameter can be used to specify an encoding (default "utf8"), an octal mode (default unspecified and implementation dependent), and a consistency hash, which is used to the current state of the file before overwriting it. If a consistency hash is provided but does not match the hash of the file on disk, a FileSystemError.CONTENTS_MODIFIED error is passed to the callback.

path string
data string
options {encoding : string=,mode : number=,expectedHash : object=,expectedContents : string=}
callback function(?string,FileSystemStats=,boolean)
    function writeFile(path, data, options, callback) {
        var encoding = options.encoding || "utf8",
            preserveBOM = options.preserveBOM;

        function _finishWrite(created) {
            appshell.fs.writeFile(path, data, encoding, preserveBOM, function (err) {
                if (err) {
                    callback(_mapError(err));
                } else {
                    stat(path, function (err, stat) {
                        callback(err, stat, created);
                    });
                }
            });
        }

        stat(path, function (err, stats) {
            if (err) {
                switch (err) {
                case FileSystemError.NOT_FOUND:
                    _finishWrite(true);
                    break;
                default:
                    callback(err);
                }
                return;
            }

            if (options.hasOwnProperty("expectedHash") && options.expectedHash !== stats._hash) {
                console.error("Blind write attempted: ", path, stats._hash, options.expectedHash);

                if (options.hasOwnProperty("expectedContents")) {
                    appshell.fs.readFile(path, encoding, function (_err, _data) {
                        if (_err || _data !== options.expectedContents) {
                            callback(FileSystemError.CONTENTS_MODIFIED);
                            return;
                        }

                        _finishWrite(false);
                    });
                    return;
                } else {
                    callback(FileSystemError.CONTENTS_MODIFIED);
                    return;
                }
            }

            _finishWrite(false);
        });
    }