text: /*
   * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved.
   *  
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"), 
   * to deal in the Software without restriction, including without limitation 
   * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
   * and/or sell copies of the Software, and to permit persons to whom the 
   * Software is furnished to do so, subject to the following conditions:
   *  
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *  
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
   * DEALINGS IN THE SOFTWARE.
   * 
   */
  
  
  /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */
  /*global define, brackets: true, $, window, navigator, Mustache, jQuery */
  
  // TODO: (issue #264) break out the definition of brackets into a separate module from the application controller logic
  
  /**
   * 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.
   */
  define(function (require, exports, module) {
      "use strict";
  
      // Load dependent non-module scripts
      require("widgets/bootstrap-dropdown");
      require("widgets/bootstrap-modal");
      require("widgets/bootstrap-twipsy-mod");
      require("thirdparty/path-utils/path-utils.min");
      require("thirdparty/smart-auto-complete-local/jquery.smart_autocomplete");
  
      // Load CodeMirror add-ons--these attach themselves to the CodeMirror module    
      require("thirdparty/CodeMirror2/addon/fold/xml-fold");
      require("thirdparty/CodeMirror2/addon/edit/matchtags");
      require("thirdparty/CodeMirror2/addon/edit/matchbrackets");
      require("thirdparty/CodeMirror2/addon/edit/closebrackets");
      require("thirdparty/CodeMirror2/addon/edit/closetag");
      require("thirdparty/CodeMirror2/addon/scroll/scrollpastend");
      require("thirdparty/CodeMirror2/addon/selection/active-line");
      require("thirdparty/CodeMirror2/addon/selection/mark-selection");
      require("thirdparty/CodeMirror2/addon/mode/multiplex");
      require("thirdparty/CodeMirror2/addon/mode/overlay");
      require("thirdparty/CodeMirror2/addon/search/match-highlighter");
      require("thirdparty/CodeMirror2/addon/search/searchcursor");
      require("thirdparty/CodeMirror2/keymap/sublime");
  
      // Load dependent modules
      var AppInit                 = require("utils/AppInit"),
          LanguageManager         = require("language/LanguageManager"),
          ProjectManager          = require("project/ProjectManager"),
          DocumentManager         = require("document/DocumentManager"),
          EditorManager           = require("editor/EditorManager"),
          JSUtils                 = require("language/JSUtils"),
          WorkingSetView          = require("project/WorkingSetView"),
          DocumentCommandHandlers = require("document/DocumentCommandHandlers"),
          FileViewController      = require("project/FileViewController"),
          FileSyncManager         = require("project/FileSyncManager"),
          KeyBindingManager       = require("command/KeyBindingManager"),
          Commands                = require("command/Commands"),
          CommandManager          = require("command/CommandManager"),
          CodeHintManager         = require("editor/CodeHintManager"),
          PerfUtils               = require("utils/PerfUtils"),
          FileSystem              = require("filesystem/FileSystem"),
          Menus                   = require("command/Menus"),
          MainViewHTML            = require("text!htmlContent/main-view.html"),
          Strings                 = require("strings"),
          Dialogs                 = require("widgets/Dialogs"),
          DefaultDialogs          = require("widgets/DefaultDialogs"),
          ExtensionLoader         = require("utils/ExtensionLoader"),
          Async                   = require("utils/Async"),
          UpdateNotification      = require("utils/UpdateNotification"),
          UrlParams               = require("utils/UrlParams").UrlParams,
          PreferencesManager      = require("preferences/PreferencesManager"),
          ExtensionUtils          = require("utils/ExtensionUtils"),
          DragAndDrop             = require("utils/DragAndDrop"),
          CodeInspection          = require("language/CodeInspection"),
          NativeApp               = require("utils/NativeApp"),
          DeprecationWarning      = require("utils/DeprecationWarning"),
          ViewCommandHandlers     = require("view/ViewCommandHandlers"),
          MainViewManager         = require("view/MainViewManager");
  
      // load modules for later use
      require("utils/Global");
      require("editor/CSSInlineEditor");
      require("project/WorkingSetSort");
      require("search/QuickOpen");
      require("file/FileUtils");
      require("project/SidebarView");
      require("utils/Resizer");
      require("LiveDevelopment/main");
      require("utils/NodeConnection");
      require("utils/NodeDomain");
      require("utils/ColorUtils");
      require("view/ThemeManager");
      require("thirdparty/lodash");
      require("language/XMLUtils");
      
      // DEPRECATED: In future we want to remove the global CodeMirror, but for now we
      // expose our required CodeMirror globally so as to avoid breaking extensions in the
      // interim.
      var CodeMirror = require("thirdparty/CodeMirror2/lib/codemirror");
  
      Object.defineProperty(window, "CodeMirror", {
          get: function () {
              DeprecationWarning.deprecationWarning('Use brackets.getModule("thirdparty/CodeMirror2/lib/codemirror") instead of global CodeMirror.', true);
              return CodeMirror;
          }
      });
      
      // Load modules that self-register and just need to get included in the main project
      require("command/DefaultMenus");
      require("document/ChangedDocumentTracker");
      require("editor/EditorStatusBar");
      require("editor/EditorCommandHandlers");
      require("editor/EditorOptionHandlers");
      require("help/HelpCommandHandlers");
      require("search/FindInFilesUI");
      require("search/FindReplace");
      require("extensibility/InstallExtensionDialog");
      require("extensibility/ExtensionManagerDialog");
      require("editor/ImageViewer");
      
      // Compatibility shims for filesystem API migration
      require("project/FileIndexManager");
      require("file/NativeFileSystem");
      require("file/NativeFileError");
      
      // Compatibility shim for PanelManager to WorkspaceManager migration
      require("view/PanelManager");
      
      PerfUtils.addMeasurement("brackets module dependencies resolved");
      
      // Local variables
      var params = new UrlParams();
      
      // read URL params
      params.parse();
      
      /**
       * 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         : CodeHintManager,
              CodeInspection          : CodeInspection,
              CommandManager          : CommandManager,
              Commands                : Commands,
              CSSUtils                : require("language/CSSUtils"),
              DefaultDialogs          : DefaultDialogs,
              Dialogs                 : Dialogs,
              DocumentCommandHandlers : DocumentCommandHandlers,
              DocumentManager         : DocumentManager,
              DocumentModule          : require("document/Document"),
              DOMAgent                : require("LiveDevelopment/Agents/DOMAgent"),
              DragAndDrop             : DragAndDrop,
              EditorManager           : EditorManager,
              ExtensionLoader         : ExtensionLoader,
              ExtensionUtils          : ExtensionUtils,
              File                    : require("filesystem/File"),
              FileFilters             : require("search/FileFilters"),
              FileSyncManager         : FileSyncManager,
              FileSystem              : FileSystem,
              FileViewController      : FileViewController,
              FileUtils               : require("file/FileUtils"),
              FindInFiles             : require("search/FindInFiles"),
              FindInFilesUI           : require("search/FindInFilesUI"),
              HTMLInstrumentation     : require("language/HTMLInstrumentation"),
              Inspector               : require("LiveDevelopment/Inspector/Inspector"),
              InstallExtensionDialog  : require("extensibility/InstallExtensionDialog"),
              JSUtils                 : JSUtils,
              KeyBindingManager       : KeyBindingManager,
              LanguageManager         : LanguageManager,
              LiveDevelopment         : require("LiveDevelopment/LiveDevelopment"),
              LiveDevMultiBrowser     : require("LiveDevelopment/LiveDevMultiBrowser"),
              LiveDevServerManager    : require("LiveDevelopment/LiveDevServerManager"),
              MainViewManager         : MainViewManager,
              MainViewFactory         : require("view/MainViewFactory"),
              Menus                   : Menus,
              MultiRangeInlineEditor  : require("editor/MultiRangeInlineEditor").MultiRangeInlineEditor,
              NativeApp               : NativeApp,
              PerfUtils               : PerfUtils,
              PreferencesManager      : PreferencesManager,
              ProjectManager          : ProjectManager,
              RemoteAgent             : require("LiveDevelopment/Agents/RemoteAgent"),
              ScrollTrackMarkers      : require("search/ScrollTrackMarkers"),
              UpdateNotification      : require("utils/UpdateNotification"),
              WorkingSetView          : 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(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 (!params.get("skipUpdateCheck") && !brackets.inBrowser) {
              AppInit.appReady(function () {
                  // launches periodic checks for updates cca every 24 hours
                  UpdateNotification.launchAutomaticUpdate();
              });
          }
      }
      
      /**
       * Setup event handlers prior to dispatching AppInit.HTML_READY
       */
      function _beforeHTMLReady() {
          // Add the platform (mac or win) 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, 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);
      });
  });
  