ProjectManager glues together the project model and file tree view and integrates as needed with other parts of Brackets. It is responsible for creating and updating the project tree when projects are opened and when changes occur to the file tree.
This module dispatches these events:
_projectRoot
changes, but working set files still open_projectRoot
changes; working set already cleared
& project root unwatched_projectRoot
changes and the tree is re-renderedTo listen for events, do something like this: (see EventDispatcher for details on this pattern) ProjectManager.on("eventname", handler);
var ERR_TYPE_CREATE = 1,
ERR_TYPE_CREATE_EXISTS = 2,
ERR_TYPE_RENAME = 3,
ERR_TYPE_DELETE = 4,
ERR_TYPE_LOADING_PROJECT = 5,
ERR_TYPE_LOADING_PROJECT_NATIVE = 6,
ERR_TYPE_MAX_FILES = 7,
ERR_TYPE_OPEN_DIALOG = 8,
ERR_TYPE_INVALID_FILENAME = 9,
ERR_TYPE_MOVE = 10;
var SETTINGS_FILENAME = "." + PreferencesManager.SETTINGS_FILENAME;
Name of the preferences for sorting directories first
var SORT_DIRECTORIES_FIRST = "sortDirectoriesFirst";
function _currentFileChange(e, curFile) {
actionCreator.setCurrentFile(curFile);
}
function _displayCreationError(e, errorInfo) {
window.setTimeout(function () {
var error = errorInfo.type,
isFolder = errorInfo.isFolder,
name = errorInfo.name;
if (error === FileSystemError.ALREADY_EXISTS) {
_showErrorDialog(ERR_TYPE_CREATE_EXISTS, isFolder, null, name);
} else if (error === ProjectModel.ERROR_INVALID_FILENAME) {
_showErrorDialog(ERR_TYPE_INVALID_FILENAME, isFolder, ProjectModel._invalidChars);
} else {
var errString = error === FileSystemError.NOT_WRITABLE ?
Strings.NO_MODIFICATION_ALLOWED_ERR :
StringUtils.format(Strings.GENERIC_ERROR, error);
_showErrorDialog(ERR_TYPE_CREATE, isFolder, errString, name).getPromise();
}
}, 10);
}
function _documentSelectionFocusChange() {
var curFullPath = MainViewManager.getCurrentlyViewedPath(MainViewManager.ACTIVE_PANE);
if (curFullPath && _hasFileSelectionFocus()) {
actionCreator.setSelected(curFullPath, true);
} else {
actionCreator.setSelected(null);
}
_fileViewControllerChange();
}
function _fileViewControllerChange() {
actionCreator.setFocused(_hasFileSelectionFocus());
_renderTree();
}
After failing to load a project, this function determines which project path to fallback to.
function _getFallbackProjectPath() {
var fallbackPaths = [],
recentProjects = PreferencesManager.getViewState("recentProjects") || [],
deferred = new $.Deferred();
// Build ordered fallback path array
if (recentProjects.length > 1) {
// *Most* recent project is the one that just failed to load, so use second most recent
fallbackPaths.push(recentProjects[1]);
}
// Next is Getting Started project
fallbackPaths.push(_getWelcomeProjectPath());
// Helper func for Async.firstSequentially()
function processItem(path) {
var deferred = new $.Deferred(),
fileEntry = FileSystem.getDirectoryForPath(path);
fileEntry.exists(function (err, exists) {
if (!err && exists) {
deferred.resolve();
} else {
deferred.reject();
}
});
return deferred.promise();
}
// Find first path that exists
Async.firstSequentially(fallbackPaths, processItem)
.done(function (fallbackPath) {
deferred.resolve(fallbackPath);
})
.fail(function () {
// Last resort is Brackets source folder which is guaranteed to exist
deferred.resolve(FileUtils.getNativeBracketsDirectoryPath());
});
return deferred.promise();
}
function _getProjectViewStateContext() {
return { location : { scope: "user",
layer: "project",
layerID: model.projectRoot.fullPath } };
}
function _getWelcomeProjectPath() {
return ProjectModel._getWelcomeProjectPath(Urls.GETTING_STARTED, FileUtils.getNativeBracketsDirectoryPath());
}
function _hasFileSelectionFocus() {
return FileViewController.getFileSelectionFocus() === FileViewController.PROJECT_MANAGER;
}
Loads the given folder as a project. Does NOT prompt about any unsaved changes - use openProject() instead to check for unsaved changes and (optionally) let the user choose the folder to open.
function _loadProject(rootPath, isUpdating) {
var result = new $.Deferred(),
startLoad = new $.Deferred();
// Some legacy code calls this API with a non-canonical path
rootPath = ProjectModel._ensureTrailingSlash(rootPath);
var projectPrefFullPath = (rootPath + SETTINGS_FILENAME),
file = FileSystem.getFileForPath(projectPrefFullPath);
//Verify that the project preferences file (.brackets.json) is NOT corrupted.
//If corrupted, display the error message and open the file in editor for the user to edit.
FileUtils.readAsText(file)
.done(function (text) {
try {
if (text) {
JSON.parse(text);
}
} catch (err) {
// Cannot parse the text read from the project preferences file.
var info = MainViewManager.findInAllWorkingSets(projectPrefFullPath);
var paneId;
if (info.length) {
paneId = info[0].paneId;
}
FileViewController.openFileAndAddToWorkingSet(projectPrefFullPath, paneId)
.done(function () {
Dialogs.showModalDialog(
DefaultDialogs.DIALOG_ID_ERROR,
Strings.ERROR_PREFS_CORRUPT_TITLE,
Strings.ERROR_PROJ_PREFS_CORRUPT
).done(function () {
// give the focus back to the editor with the pref file
MainViewManager.focusActivePane();
});
});
}
});
if (isUpdating) {
// We're just refreshing. Don't need to unwatch the project root, so we can start loading immediately.
startLoad.resolve();
} else {
if (model.projectRoot && model.projectRoot.fullPath === rootPath) {
return (new $.Deferred()).resolve().promise();
}
// About to close current project (if any)
if (model.projectRoot) {
exports.trigger("beforeProjectClose", model.projectRoot);
}
// close all the old files
MainViewManager._closeAll(MainViewManager.ALL_PANES);
_unwatchProjectRoot().always(function () {
// Done closing old project (if any)
if (model.projectRoot) {
LanguageManager._resetPathLanguageOverrides();
PreferencesManager._reloadUserPrefs(model.projectRoot);
exports.trigger("projectClose", model.projectRoot);
}
startLoad.resolve();
});
}
startLoad.done(function () {
var context = { location : { scope: "user",
layer: "project" } };
// Clear project path map
if (!isUpdating) {
PreferencesManager._stateProjectLayer.setProjectPath(rootPath);
}
// Populate file tree as long as we aren't running in the browser
if (!brackets.inBrowser) {
if (!isUpdating) {
_watchProjectRoot(rootPath);
}
// Point at a real folder structure on local disk
var rootEntry = FileSystem.getDirectoryForPath(rootPath);
rootEntry.exists(function (err, exists) {
if (exists) {
var projectRootChanged = (!model.projectRoot || !rootEntry) ||
model.projectRoot.fullPath !== rootEntry.fullPath;
// Success!
var perfTimerName = PerfUtils.markStart("Load Project: " + rootPath);
_projectWarnedForTooManyFiles = false;
_setProjectRoot(rootEntry).always(function () {
model.setBaseUrl(PreferencesManager.getViewState("project.baseUrl", context) || "");
if (projectRootChanged) {
_reloadProjectPreferencesScope();
PreferencesManager._setCurrentFile(rootPath);
}
// If this is the most current welcome project, record it. In future launches, we want
// to substitute the latest welcome project from the current build instead of using an
// outdated one (when loading recent projects or the last opened project).
if (rootPath === _getWelcomeProjectPath()) {
addWelcomeProjectPath(rootPath);
}
if (projectRootChanged) {
// Allow asynchronous event handlers to finish before resolving result by collecting promises from them
exports.trigger("projectOpen", model.projectRoot);
result.resolve();
} else {
exports.trigger("projectRefresh", model.projectRoot);
result.resolve();
}
PerfUtils.addMeasurement(perfTimerName);
});
} else {
console.log("error loading project");
_showErrorDialog(ERR_TYPE_LOADING_PROJECT_NATIVE, true, err || FileSystemError.NOT_FOUND, rootPath)
.done(function () {
// Reset _projectRoot to null so that the following _loadProject call won't
// run the 'beforeProjectClose' event a second time on the original project,
// which is now partially torn down (see #6574).
model.projectRoot = null;
// The project folder stored in preference doesn't exist, so load the default
// project directory.
// TODO (issue #267): When Brackets supports having no project directory
// defined this code will need to change
_getFallbackProjectPath().done(function (path) {
_loadProject(path).always(function () {
// Make sure not to reject the original deferred until the fallback
// project is loaded, so we don't violate expectations that there is always
// a current project before continuing after _loadProject().
result.reject();
});
});
});
}
});
}
});
return result.promise();
}
Invoke project settings dialog.
function _projectSettings() {
return PreferencesDialogs.showProjectPreferencesDialog(getBaseUrl()).getPromise();
}
function _reloadProjectPreferencesScope() {
var root = getProjectRoot();
if (root) {
// Alias the "project" Scope to the path Scope for the project-level settings file
PreferencesManager._setProjectSettingsFile(root.fullPath + SETTINGS_FILENAME);
} else {
PreferencesManager._setProjectSettingsFile();
}
}
function _revertSelection(previousPath, switchToWorkingSet) {
model.setSelected(previousPath);
if (switchToWorkingSet) {
FileViewController.setFileViewFocus(FileViewController.WORKING_SET_VIEW);
}
}
var _saveProjectPath = function () {
// save the current project
PreferencesManager.setViewState("projectPath", model.projectRoot.fullPath);
};
function _setFileTreeSelectionWidth(width) {
model.setSelectionWidth(width);
_renderTreeSync();
}
// Initialize variables and listeners that depend on the HTML DOM
AppInit.htmlReady(function () {
$projectTreeContainer = $("#project-files-container");
$projectTreeContainer.addClass("jstree jstree-brackets");
$projectTreeContainer.css("overflow", "auto");
$projectTreeContainer.css("position", "relative");
fileTreeViewContainer = $("<div>").appendTo($projectTreeContainer)[0];
model.setSelectionWidth($projectTreeContainer.width());
$(".main-view").click(function (jqEvent) {
if (!jqEvent.target.classList.contains("jstree-rename-input")) {
forceFinishRename();
actionCreator.setContext(null);
}
});
$("#working-set-list-container").on("contentChanged", function () {
$projectTreeContainer.trigger("contentChanged");
});
Menus.getContextMenu(Menus.ContextMenuIds.PROJECT_MENU).on("beforeContextMenuOpen", function () {
actionCreator.restoreContext();
});
Menus.getContextMenu(Menus.ContextMenuIds.PROJECT_MENU).on("beforeContextMenuClose", function () {
model.setContext(null, false, true);
});
$projectTreeContainer.on("contextmenu", function () {
forceFinishRename();
});
$projectTreeContainer.on("dragover", function(e) {
e.preventDefault();
});
// Add support for moving items to root directory
$projectTreeContainer.on("drop", function(e) {
var data = JSON.parse(e.originalEvent.dataTransfer.getData("text"));
actionCreator.moveItem(data.path, getProjectRoot().fullPath);
e.stopPropagation();
});
// When a context menu item is selected, we need to clear the context
// because we don't get a beforeContextMenuClose event since Bootstrap
// handles this directly.
$("#project-context-menu").on("click.dropdown-menu", function () {
model.setContext(null, true);
});
$projectTreeContainer.on("scroll", function () {
// Close open menus on scroll and clear the context, but only if there's a menu open.
if ($(".dropdown.open").length > 0) {
Menus.closeAll();
actionCreator.setContext(null);
}
// we need to render the tree without a delay to not cause selection extension issues (#10573)
_renderTreeSync();
});
_renderTree();
ViewUtils.addScrollerShadow($projectTreeContainer[0]);
});
EventDispatcher.makeEventDispatcher(exports);
// Init default project path to welcome project
PreferencesManager.stateManager.definePreference("projectPath", "string", _getWelcomeProjectPath());
exports.on("projectOpen", _reloadProjectPreferencesScope);
exports.on("projectOpen", _saveProjectPath);
exports.on("beforeAppClose", _unwatchProjectRoot);
// Due to circular dependencies, not safe to call on() directly for other modules' events
EventDispatcher.on_duringInit(FileViewController, "documentSelectionFocusChange", _documentSelectionFocusChange);
EventDispatcher.on_duringInit(FileViewController, "fileViewFocusChange", _fileViewControllerChange);
EventDispatcher.on_duringInit(MainViewManager, "currentFileChange", _currentFileChange);
// Commands
CommandManager.register(Strings.CMD_OPEN_FOLDER, Commands.FILE_OPEN_FOLDER, openProject);
CommandManager.register(Strings.CMD_PROJECT_SETTINGS, Commands.FILE_PROJECT_SETTINGS, _projectSettings);
CommandManager.register(Strings.CMD_FILE_REFRESH, Commands.FILE_REFRESH, refreshFileTree);
// Define the preference to decide how to sort the Project Tree files
PreferencesManager.definePreference(SORT_DIRECTORIES_FIRST, "boolean", brackets.platform !== "mac", {
description: Strings.DESCRIPTION_SORT_DIRECTORIES_FIRST
})
.on("change", function () {
actionCreator.setSortDirectoriesFirst(PreferencesManager.get(SORT_DIRECTORIES_FIRST));
});
actionCreator.setSortDirectoriesFirst(PreferencesManager.get(SORT_DIRECTORIES_FIRST));
function _setProjectRoot(rootEntry) {
var d = new $.Deferred();
model.setProjectRoot(rootEntry).then(function () {
d.resolve();
model.reopenNodes(PreferencesManager.getViewState("project.treeState", _getProjectViewStateContext()));
});
return d.promise();
}
function _unwatchProjectRoot() {
var result = new $.Deferred();
if (!model.projectRoot) {
result.reject();
} else {
FileSystem.off("change", _fileSystemChange);
FileSystem.off("rename", _fileSystemRename);
FileSystem.unwatch(model.projectRoot, function (err) {
if (err) {
console.error("Error unwatching project root: ", model.projectRoot.fullPath, err);
result.reject(err);
} else {
result.resolve();
}
});
// Reset allFiles cache
model._resetCache();
}
return result.promise();
}
function _watchProjectRoot(rootPath) {
FileSystem.on("change", _fileSystemChange);
FileSystem.on("rename", _fileSystemRename);
FileSystem.watch(FileSystem.getDirectoryForPath(rootPath), ProjectModel._shouldShowName, ProjectModel.defaultIgnoreGlobs, function (err) {
if (err === FileSystemError.TOO_MANY_ENTRIES) {
if (!_projectWarnedForTooManyFiles) {
_showErrorDialog(ERR_TYPE_MAX_FILES);
_projectWarnedForTooManyFiles = true;
}
} else if (err) {
console.error("Error watching project root: ", rootPath, err);
}
});
// Reset allFiles cache
model._resetCache();
}
Adds a CSS class provider, invoked before each tree item is rendered.
function addClassesProvider(callback) {
return FileTreeView.addClassesProvider(callback);
}
Adds an icon provider. The callback is invoked before each tree item is rendered, and can return content to prepend to the item.
function addIconProvider(callback) {
return FileTreeView.addIconProvider(callback);
}
Adds the path to the list of welcome projects we've ever seen, if not on the list already.
function addWelcomeProjectPath(path) {
var welcomeProjects = ProjectModel._addWelcomeProjectPath(path,
PreferencesManager.getViewState("welcomeProjects"));
PreferencesManager.setViewState("welcomeProjects", welcomeProjects);
}
Create a new item in the current project.
function createNewItem(baseDir, initialName, skipRename, isFolder) {
baseDir = model.getDirectoryInProject(baseDir);
if (skipRename) {
if(isFolder) {
return model.createAtPath(baseDir + initialName + "/");
}
return model.createAtPath(baseDir + initialName);
}
return actionCreator.startCreating(baseDir, initialName, isFolder);
}
Delete file or directore from project
function deleteItem(entry) {
var result = new $.Deferred();
entry.moveToTrash(function (err) {
if (!err) {
DocumentManager.notifyPathDeleted(entry.fullPath);
result.resolve();
} else {
_showErrorDialog(ERR_TYPE_DELETE, entry.isDirectory, FileUtils.getFileErrorString(err), entry.fullPath);
result.reject(err);
}
});
return result.promise();
}
Causes the rename operation that's in progress to complete.
function forceFinishRename() {
actionCreator.performRename();
}
Returns an Array of all files for this project, optionally including files in the working set that are not under the project root. Files are filtered first by ProjectModel.shouldShow(), then by the custom filter argument (if one was provided).
function getAllFiles(filter, includeWorkingSet, sort) {
var viewFiles, deferred;
// The filter and includeWorkingSet params are both optional.
// Handle the case where filter is omitted but includeWorkingSet is
// specified.
if (includeWorkingSet === undefined && typeof (filter) !== "function") {
includeWorkingSet = filter;
filter = null;
}
if (includeWorkingSet) {
viewFiles = MainViewManager.getWorkingSet(MainViewManager.ALL_PANES);
}
deferred = new $.Deferred();
model.getAllFiles(filter, viewFiles, sort)
.done(function (fileList) {
deferred.resolve(fileList);
})
.fail(function (err) {
if (err === FileSystemError.TOO_MANY_ENTRIES && !_projectWarnedForTooManyFiles) {
_showErrorDialog(ERR_TYPE_MAX_FILES);
_projectWarnedForTooManyFiles = true;
}
// resolve with empty list
deferred.resolve([]);
});
return deferred.promise();
}
Returns the encoded Base URL of the currently loaded project, or empty string if no project is open (during startup, or running outside of app shell).
function getBaseUrl() {
return model.getBaseUrl();
}
Gets the filesystem object for the current context in the file tree.
function getContext() {
return model.getContext();
}
Returns the File or Directory corresponding to the item that was right-clicked on in the file tree menu.
function getFileTreeContext() {
var selectedEntry = model.getContext();
return selectedEntry;
}
Initial project path is stored in prefs, which defaults to the welcome project on first launch.
function getInitialProjectPath() {
return updateWelcomeProjectPath(PreferencesManager.getViewState("projectPath"));
}
Returns a filter for use with getAllFiles() that filters files based on LanguageManager language id
function getLanguageFilter(languageId) {
return function languageFilter(file) {
var id = LanguageManager.getLanguageForPath(file.fullPath).getId();
if (typeof languageId === "string") {
return (id === languageId);
} else {
return (languageId.indexOf(id) !== -1);
}
};
}
Returns the root folder of the currently loaded project, or null if no project is open (during startup, or running outside of app shell).
function getProjectRoot() {
return model.projectRoot;
}
Returns the File or Directory corresponding to the item selected in the sidebar panel, whether in the file tree OR in the working set; or null if no item is selected anywhere in the sidebar. May NOT be identical to the current Document - a folder may be selected in the sidebar, or the sidebar may not have the current document visible in the tree & working set.
function getSelectedItem() {
// Prefer file tree context, then file tree selection, else use working set
var selectedEntry = getFileTreeContext();
if (!selectedEntry) {
selectedEntry = model.getSelected();
}
if (!selectedEntry) {
selectedEntry = MainViewManager.getCurrentlyViewedFile();
}
return selectedEntry;
}
Returns true if the given path is the same as one of the welcome projects we've previously opened, or the one for the current build.
function isWelcomeProjectPath(path) {
return ProjectModel._isWelcomeProjectPath(path, _getWelcomeProjectPath(), PreferencesManager.getViewState("welcomeProjects"));
}
Returns true if absPath lies within the project, false otherwise. Does not support paths containing ".."
function isWithinProject(absPathOrEntry) {
return model.isWithinProject(absPathOrEntry);
}
If absPath lies within the project, returns a project-relative path. Else returns absPath unmodified. Does not support paths containing ".."
function makeProjectRelativeIfPossible(absPath) {
return model.makeProjectRelativeIfPossible(absPath);
}
Open a new project. Currently, Brackets must always have a project open, so this method handles both closing the current project and opening a new project.
function openProject(path) {
var result = new $.Deferred();
// Confirm any unsaved changes first. We run the command in "prompt-only" mode, meaning it won't
// actually close any documents even on success; we'll do that manually after the user also oks
// the folder-browse dialog.
CommandManager.execute(Commands.FILE_CLOSE_ALL, { promptOnly: true })
.done(function () {
if (path) {
// use specified path
_loadProject(path, false).then(result.resolve, result.reject);
} else {
// Pop up a folder browse dialog
FileSystem.showOpenDialog(false, true, Strings.CHOOSE_FOLDER, model.projectRoot.fullPath, null, function (err, files) {
if (!err) {
// If length == 0, user canceled the dialog; length should never be > 1
if (files.length > 0) {
// Load the new project into the folder tree
_loadProject(files[0]).then(result.resolve, result.reject);
} else {
result.reject();
}
} else {
_showErrorDialog(ERR_TYPE_OPEN_DIALOG, null, err);
result.reject();
}
});
}
})
.fail(function () {
result.reject();
});
// if fail, don't open new project: user canceled (or we failed to save its unsaved changes)
return result.promise();
}
Refresh the project's file tree, maintaining the current selection.
Note that the original implementation of this returned a promise to be resolved when the refresh is complete.
That use is deprecated and refreshFileTree
is now a "fire and forget" kind of function.
var refreshFileTree = function refreshFileTree() {
FileSystem.clearAllCaches();
return new $.Deferred().resolve().promise();
};
refreshFileTree = _.debounce(refreshFileTree, _refreshDelay);
Forces the file tree to rerender. Typically, the tree only rerenders the portions of the tree that have changed data. If an extension that augments the tree has changes that it needs to display, calling rerenderTree will cause the components for the whole tree to be rerendered.
function rerenderTree() {
_renderTree(true);
}
// Private API helpful in testing
exports._actionCreator = actionCreator;
exports._RENDER_DEBOUNCE_TIME = _RENDER_DEBOUNCE_TIME;
// Private API for use with SidebarView
exports._setFileTreeSelectionWidth = _setFileTreeSelectionWidth;
// Define public API
exports.getProjectRoot = getProjectRoot;
exports.getBaseUrl = getBaseUrl;
exports.setBaseUrl = setBaseUrl;
exports.isWithinProject = isWithinProject;
exports.makeProjectRelativeIfPossible = makeProjectRelativeIfPossible;
exports.shouldShow = ProjectModel.shouldShow;
exports.openProject = openProject;
exports.getFileTreeContext = getFileTreeContext;
exports.getSelectedItem = getSelectedItem;
exports.getContext = getContext;
exports.getInitialProjectPath = getInitialProjectPath;
exports.isWelcomeProjectPath = isWelcomeProjectPath;
exports.updateWelcomeProjectPath = updateWelcomeProjectPath;
exports.createNewItem = createNewItem;
exports.renameItemInline = renameItemInline;
exports.deleteItem = deleteItem;
exports.forceFinishRename = forceFinishRename;
exports.showInTree = showInTree;
exports.refreshFileTree = refreshFileTree;
exports.getAllFiles = getAllFiles;
exports.getLanguageFilter = getLanguageFilter;
exports.addIconProvider = addIconProvider;
exports.addClassesProvider = addClassesProvider;
exports.rerenderTree = rerenderTree;
});
Sets the encoded Base URL of the currently loaded project.
function setBaseUrl(projectBaseUrl) {
var context = _getProjectViewStateContext();
projectBaseUrl = model.setBaseUrl(projectBaseUrl);
PreferencesManager.setViewState("project.baseUrl", projectBaseUrl, context);
}
Expands tree nodes to show the given file or folder and selects it. Silently no-ops if the path lies outside the project, or if it doesn't exist.
function showInTree(entry) {
return model.showInTree(entry).then(_saveTreeState);
}
function ActionCreator(model) {
this.model = model;
this._bindEvents();
}
ActionCreator.prototype._bindEvents = function () {
// Change events are the standard Flux signal to rerender the view. Note that
// current Flux style is to have the view itself listen to the Store for change events
// and re-render itself.
this.model.on(ProjectModel.EVENT_CHANGE, function () {
_renderTree();
});
// The "should select" event signals that we need to open the document based on file tree
// activity.
this.model.on(ProjectModel.EVENT_SHOULD_SELECT, function (e, data) {
if (data.add) {
FileViewController.openFileAndAddToWorkingSet(data.path).fail(_.partial(_revertSelection, data.previousPath, !data.hadFocus));
} else {
FileViewController.openAndSelectDocument(data.path, FileViewController.PROJECT_MANAGER).fail(_.partial(_revertSelection, data.previousPath, !data.hadFocus));
}
});
this.model.on(ProjectModel.EVENT_SHOULD_FOCUS, function () {
FileViewController.setFileViewFocus(FileViewController.PROJECT_MANAGER);
});
this.model.on(ProjectModel.ERROR_CREATION, _displayCreationError);
};
See ProjectModel.cancelRename
ActionCreator.prototype.cancelRename = function () {
this.model.cancelRename();
};
See ProjectModel.closeSubtree
ActionCreator.prototype.closeSubtree = function (path) {
this.model.closeSubtree(path);
_saveTreeState();
};
ActionCreator.prototype.dragItem = function (path) {
// Close open menus on drag and clear the context, but only if there's a menu open.
if ($(".dropdown.open").length > 0) {
Menus.closeAll();
this.setContext(null);
}
// Close directory, if dragged item is directory
if (_.last(path) === '/') {
this.setDirectoryOpen(path, false);
}
};
Moves the item in the oldPath to the newDirectory directory
ActionCreator.prototype.moveItem = function (oldPath, newDirectory) {
var fileName = FileUtils.getBaseName(oldPath),
newPath = newDirectory + fileName,
self = this;
// If item dropped onto itself or onto its parent directory, return
if (oldPath === newDirectory || FileUtils.getParentPath(oldPath) === newDirectory) {
return;
}
// Add trailing slash if directory is moved
if (_.last(oldPath) === '/') {
newPath = ProjectModel._ensureTrailingSlash(newPath);
}
this.startRename(oldPath, true);
this.setRenameValue(newPath);
this.performRename();
this.setDirectoryOpen(newDirectory, true);
};
See ProjectModel.performRename
ActionCreator.prototype.performRename = function () {
return this.model.performRename();
};
See ProjectModel.refresh
ActionCreator.prototype.refresh = function () {
this.model.refresh();
};
See ProjectModel.restoreContext
ActionCreator.prototype.restoreContext = function () {
this.model.restoreContext();
};
See ProjectModel.selectInWorkingSet
ActionCreator.prototype.selectInWorkingSet = function (path) {
this.model.selectInWorkingSet(path);
};
See ProjectModel.setContext
ActionCreator.prototype.setContext = function (path) {
this.model.setContext(path);
};
See ProjectModel.setCurrentFile
ActionCreator.prototype.setCurrentFile = function (curFile) {
this.model.setCurrentFile(curFile);
};
Sets the directory at the given path to open in the tree and saves the open nodes to view state.
See ProjectModel.setDirectoryOpen
ActionCreator.prototype.setDirectoryOpen = function (path, open) {
this.model.setDirectoryOpen(path, open).then(_saveTreeState);
};
See ProjectModel.setFocused
ActionCreator.prototype.setFocused = function (focused) {
this.model.setFocused(focused);
};
See ProjectModel.setRenameValue
ActionCreator.prototype.setRenameValue = function (path) {
this.model.setRenameValue(path);
};
See ProjectModel.setSelected
ActionCreator.prototype.setSelected = function (path, doNotOpen) {
this.model.setSelected(path, doNotOpen);
};
See ProjectModel.setSortDirectoriesFirst
ActionCreator.prototype.setSortDirectoriesFirst = function (sortDirectoriesFirst) {
this.model.setSortDirectoriesFirst(sortDirectoriesFirst);
};
See ProjectModel.startCreating
ActionCreator.prototype.startCreating = function (basedir, newName, isFolder) {
return this.model.startCreating(basedir, newName, isFolder);
};
See ProjectModel.startRename
ActionCreator.prototype.startRename = function (path, isMoved) {
// This is very not Flux-like, which is a sign that Flux may not be the
// right choice here *or* that this architecture needs to evolve subtly
// in how errors are reported (more like the create case).
// See #9284.
renameItemInline(path, isMoved);
};