brackets is the root of the Brackets codebase. This file pulls in all other modules as dependencies (or dependencies thereof), initializes the UI, and binds global menus & keyboard shortcuts to their Commands.
Unlike other modules, this one can be accessed without an explicit require() because it exposes a global object, window.brackets.
Setup event handlers prior to dispatching AppInit.HTML_READY
function _beforeHTMLReady() {
// Add the platform (mac, win or linux) to the body tag so we can have platform-specific CSS rules
$("body").addClass("platform-" + brackets.platform);
// Browser-hosted version may also have different CSS (e.g. since '#titlebar' is shown)
if (brackets.inBrowser) {
$("body").addClass("in-browser");
} else {
$("body").addClass("in-appshell");
}
// Enable/Disable HTML Menus
if (brackets.nativeMenus) {
$("body").addClass("has-appshell-menus");
} else {
// (issue #5310) workaround for bootstrap dropdown: prevent the menu item to grab
// the focus -- override jquery focus implementation for top-level menu items
(function () {
var defaultFocus = $.fn.focus;
$.fn.focus = function () {
if (!this.hasClass("dropdown-toggle")) {
return defaultFocus.apply(this, arguments);
}
};
}());
}
// Localize MainViewHTML and inject into <BODY> tag
$("body").html(Mustache.render(MainViewHTML, { shouldAddAA: (brackets.platform === "mac"), Strings: Strings }));
// Update title
$("title").text(brackets.config.app_title);
// Respond to dragging & dropping files/folders onto the window by opening them. If we don't respond
// to these events, the file would load in place of the Brackets UI
DragAndDrop.attachHandlers();
// TODO: (issue 269) to support IE, need to listen to document instead (and even then it may not work when focus is in an input field?)
$(window).focus(function () {
// This call to syncOpenDocuments() *should* be a no-op now that we have
// file watchers, but is still here as a safety net.
FileSyncManager.syncOpenDocuments();
});
// Prevent unhandled middle button clicks from triggering native behavior
// Example: activating AutoScroll (see #510)
$("html").on("mousedown", ".inline-widget", function (e) {
if (e.button === 1) {
e.preventDefault();
}
});
// The .no-focus style is added to clickable elements that should
// not steal focus. Calling preventDefault() on mousedown prevents
// focus from going to the click target.
$("html").on("mousedown", ".no-focus", function (e) {
// Text fields should always be focusable.
var $target = $(e.target),
isFormElement =
$target.is("input") ||
$target.is("textarea") ||
$target.is("select");
if (!isFormElement) {
e.preventDefault();
}
});
// Prevent clicks on any link from navigating to a different page (which could lose unsaved
// changes). We can't use a simple .on("click", "a") because of http://bugs.jquery.com/ticket/3861:
// jQuery hides non-left clicks from such event handlers, yet middle-clicks still cause CEF to
// navigate. Also, a capture handler is more reliable than bubble.
window.document.body.addEventListener("click", function (e) {
// Check parents too, in case link has inline formatting tags
var node = e.target, url;
while (node) {
if (node.tagName === "A") {
url = node.getAttribute("href");
if (url && !url.match(/^#/)) {
NativeApp.openURLInDefaultBrowser(url);
}
e.preventDefault();
break;
}
node = node.parentElement;
}
}, true);
// Prevent extensions from using window.open() to insecurely load untrusted web content
var real_windowOpen = window.open;
window.open = function (url) {
// Allow file:// URLs, relative URLs (implicitly file: also), and about:blank
if (!url.match(/^file:\/\//) && !url.match(/^about:blank/) && url.indexOf(":") !== -1) {
throw new Error("Brackets-shell is not a secure general purpose web browser. Use NativeApp.openURLInDefaultBrowser() to open URLs in the user's main browser");
}
return real_windowOpen.apply(window, arguments);
};
// jQuery patch to shim deprecated usage of $() on EventDispatchers
var DefaultCtor = jQuery.fn.init;
jQuery.fn.init = function (firstArg, secondArg) {
var jQObject = new DefaultCtor(firstArg, secondArg);
// Is this a Brackets EventDispatcher object? (not a DOM node or other object)
if (firstArg && firstArg._EventDispatcher) {
// Patch the jQ wrapper object so it calls EventDispatcher's APIs instead of jQuery's
jQObject.on = firstArg.on.bind(firstArg);
jQObject.one = firstArg.one.bind(firstArg);
jQObject.off = firstArg.off.bind(firstArg);
// Don't offer legacy support for trigger()/triggerHandler() on core model objects; extensions
// shouldn't be doing that anyway since it's basically poking at private API
// Console warning, since $() is deprecated for EventDispatcher objects
// (pass true to only print once per caller, and index 4 since the extension caller is deeper in the stack than usual)
DeprecationWarning.deprecationWarning("Deprecated: Do not use $().on/off() on Brackets modules and model objects. Call on()/off() directly on the object without a $() wrapper.", true, 4);
}
return jQObject;
};
}
// Wait for view state to load.
var viewStateTimer = PerfUtils.markStart("User viewstate loading");
PreferencesManager._smUserScopeLoading.always(function () {
PerfUtils.addMeasurement(viewStateTimer);
// Dispatch htmlReady event
_beforeHTMLReady();
AppInit._dispatchReady(AppInit.HTML_READY);
$(window.document).ready(_onReady);
});
});
Setup test object
function _initTest() {
// TODO: (issue #265) Make sure the "test" object is not included in final builds
// All modules that need to be tested from the context of the application
// must to be added to this object. The unit tests cannot just pull
// in the modules since they would run in context of the unit test window,
// and would not have access to the app html/css.
brackets.test = {
CodeHintManager : require("editor/CodeHintManager"),
CodeInspection : require("language/CodeInspection"),
CommandManager : require("command/CommandManager"),
Commands : require("command/Commands"),
CSSUtils : require("language/CSSUtils"),
DefaultDialogs : require("widgets/DefaultDialogs"),
Dialogs : require("widgets/Dialogs"),
DocumentCommandHandlers : require("document/DocumentCommandHandlers"),
DocumentManager : require("document/DocumentManager"),
DocumentModule : require("document/Document"),
DOMAgent : require("LiveDevelopment/Agents/DOMAgent"),
DragAndDrop : require("utils/DragAndDrop"),
EditorManager : require("editor/EditorManager"),
ExtensionLoader : require("utils/ExtensionLoader"),
ExtensionUtils : require("utils/ExtensionUtils"),
File : require("filesystem/File"),
FileFilters : require("search/FileFilters"),
FileSyncManager : require("project/FileSyncManager"),
FileSystem : require("filesystem/FileSystem"),
FileUtils : require("file/FileUtils"),
FileViewController : require("project/FileViewController"),
FindInFiles : require("search/FindInFiles"),
FindInFilesUI : require("search/FindInFilesUI"),
HTMLInstrumentation : require("language/HTMLInstrumentation"),
Inspector : require("LiveDevelopment/Inspector/Inspector"),
InstallExtensionDialog : require("extensibility/InstallExtensionDialog"),
JSUtils : require("language/JSUtils"),
KeyBindingManager : require("command/KeyBindingManager"),
LanguageManager : require("language/LanguageManager"),
LiveDevelopment : require("LiveDevelopment/LiveDevelopment"),
LiveDevMultiBrowser : require("LiveDevelopment/LiveDevMultiBrowser"),
LiveDevServerManager : require("LiveDevelopment/LiveDevServerManager"),
MainViewFactory : require("view/MainViewFactory"),
MainViewManager : require("view/MainViewManager"),
Menus : require("command/Menus"),
MultiRangeInlineEditor : require("editor/MultiRangeInlineEditor").MultiRangeInlineEditor,
NativeApp : require("utils/NativeApp"),
PerfUtils : require("utils/PerfUtils"),
PreferencesManager : require("preferences/PreferencesManager"),
ProjectManager : require("project/ProjectManager"),
RemoteAgent : require("LiveDevelopment/Agents/RemoteAgent"),
ScrollTrackMarkers : require("search/ScrollTrackMarkers"),
UpdateNotification : require("utils/UpdateNotification"),
WorkingSetView : require("project/WorkingSetView"),
doneLoading : false
};
AppInit.appReady(function () {
brackets.test.doneLoading = true;
});
}
Setup Brackets
function _onReady() {
PerfUtils.addMeasurement("window.document Ready");
// Let the user know Brackets doesn't run in a web browser yet
if (brackets.inBrowser) {
Dialogs.showModalDialog(
DefaultDialogs.DIALOG_ID_ERROR,
Strings.ERROR_IN_BROWSER_TITLE,
Strings.ERROR_IN_BROWSER
);
}
// Use quiet scrollbars if we aren't on Lion. If we're on Lion, only
// use native scroll bars when the mouse is not plugged in or when
// using the "Always" scroll bar setting.
var osxMatch = /Mac OS X 10\D([\d+])\D/.exec(window.navigator.userAgent);
if (osxMatch && osxMatch[1] && Number(osxMatch[1]) >= 7) {
// test a scrolling div for scrollbars
var $testDiv = $("<div style='position:fixed;left:-50px;width:50px;height:50px;overflow:auto;'><div style='width:100px;height:100px;'/></div>").appendTo(window.document.body);
if ($testDiv.outerWidth() === $testDiv.get(0).clientWidth) {
$(".sidebar").removeClass("quiet-scrollbars");
}
$testDiv.remove();
}
// Load default languages and preferences
Async.waitForAll([LanguageManager.ready, PreferencesManager.ready]).always(function () {
// Load all extensions. This promise will complete even if one or more
// extensions fail to load.
var extensionPathOverride = params.get("extensions"); // used by unit tests
var extensionLoaderPromise = ExtensionLoader.init(extensionPathOverride ? extensionPathOverride.split(",") : null);
// Load the initial project after extensions have loaded
extensionLoaderPromise.always(function () {
// Signal that extensions are loaded
AppInit._dispatchReady(AppInit.EXTENSIONS_LOADED);
// Finish UI initialization
ViewCommandHandlers.restoreFontSize();
var initialProjectPath = ProjectManager.getInitialProjectPath();
ProjectManager.openProject(initialProjectPath).always(function () {
_initTest();
// If this is the first launch, and we have an index.html file in the project folder (which should be
// the samples folder on first launch), open it automatically. (We explicitly check for the
// samples folder in case this is the first time we're launching Brackets after upgrading from
// an old version that might not have set the "afterFirstLaunch" pref.)
var deferred = new $.Deferred();
if (!params.get("skipSampleProjectLoad") && !PreferencesManager.getViewState("afterFirstLaunch")) {
PreferencesManager.setViewState("afterFirstLaunch", "true");
if (ProjectManager.isWelcomeProjectPath(initialProjectPath)) {
FileSystem.resolve(initialProjectPath + "index.html", function (err, file) {
if (!err) {
var promise = CommandManager.execute(Commands.CMD_ADD_TO_WORKINGSET_AND_OPEN, { fullPath: file.fullPath });
promise.then(deferred.resolve, deferred.reject);
} else {
deferred.reject();
}
});
} else {
deferred.resolve();
}
} else {
deferred.resolve();
}
deferred.always(function () {
// Signal that Brackets is loaded
AppInit._dispatchReady(AppInit.APP_READY);
PerfUtils.addMeasurement("Application Startup");
if (PreferencesManager._isUserScopeCorrupt()) {
var userPrefFullPath = PreferencesManager.getUserPrefFile();
// user scope can get corrupt only if the file exists, is readable,
// but malformed. no need to check for its existance.
var info = MainViewManager.findInAllWorkingSets(userPrefFullPath);
var paneId;
if (info.length) {
paneId = info[0].paneId;
}
FileViewController.openFileAndAddToWorkingSet(userPrefFullPath, paneId)
.done(function () {
Dialogs.showModalDialog(
DefaultDialogs.DIALOG_ID_ERROR,
Strings.ERROR_PREFS_CORRUPT_TITLE,
Strings.ERROR_PREFS_CORRUPT
).done(function () {
// give the focus back to the editor with the pref file
MainViewManager.focusActivePane();
});
});
}
});
// See if any startup files were passed to the application
if (brackets.app.getPendingFilesToOpen) {
brackets.app.getPendingFilesToOpen(function (err, paths) {
DragAndDrop.openDroppedFiles(paths);
});
}
});
});
});
// Check for updates
if (!brackets.inBrowser && !params.get("skipUpdateCheck")) {
AppInit.appReady(function () {
// launches periodic checks for updates cca every 24 hours
UpdateNotification.launchAutomaticUpdate();
});
}
}