Modules (181)

brackets

Description

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.

Dependencies

Functions

Private

_beforeHTMLReady

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);
    });
});
Private

_initTest

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;
        });
    }
Private

_onReady

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();
            });
        }
    }