jasmine.HtmlReporterHelpers = {};

jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
  var el = document.createElement(type);

  for (var i = 2; i < arguments.length; i++) {
    var child = arguments[i];

    if (typeof child === 'string') {
      el.appendChild(document.createTextNode(child));
    } else {
      if (child) {
        el.appendChild(child);
      }
    }
  }

  for (var attr in attrs) {
    if (attr == "className") {
      el[attr] = attrs[attr];
    } else {
      el.setAttribute(attr, attrs[attr]);
    }
  }

  return el;
};

jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
  var results = child.results();
  var status = results.passed() ? 'passed' : 'failed';
  if (results.skipped) {
    status = 'skipped';
  }

  return status;
};

jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
  var parentDiv = this.dom.summary;
  var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
  var parent = child[parentSuite];

  if (parent) {
    if (typeof this.views.suites[parent.id] == 'undefined') {
      this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
    }
    parentDiv = this.views.suites[parent.id].element;
  }

  parentDiv.appendChild(childElement);
};

jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
  for(var fn in jasmine.HtmlReporterHelpers) {
    ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
  }
};

jasmine.HtmlReporter = function(_doc) {
  var self = this;
  var doc = _doc || window.document;

  var reporterView;

  var dom = {};

  // Jasmine Reporter Public Interface
  self.logRunningSpecs = false;

  self.reportRunnerStarting = function(runner) {
    var specs = runner.specs() || [];

    if (specs.length == 0) {
      return;
    }

    createReporterDom(runner.env.versionString());
    doc.body.appendChild(dom.reporter);

    reporterView = new jasmine.HtmlReporter.ReporterView(dom);
    reporterView.addSpecs(specs, self.specFilter);
  };

  self.reportRunnerResults = function(runner) {
    reporterView.complete();
  };

  self.reportSuiteResults = function(suite) {
    reporterView.suiteComplete(suite);
  };

  self.reportSpecStarting = function(spec) {
    if (self.logRunningSpecs) {
      self.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
    }
  };

  self.reportSpecResults = function(spec) {
    reporterView.specComplete(spec);
  };

  self.log = function() {
    var console = jasmine.getGlobal().console;
    if (console && console.log) {
      if (console.log.apply) {
        console.log.apply(console, arguments);
      } else {
        console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
      }
    }
  };

  self.specFilter = function(spec) {
    if (!focusedSpecName()) {
      return true;
    }

    return spec.getFullName().indexOf(focusedSpecName()) === 0;
  };

  return self;

  function focusedSpecName() {
    var specName;

    (function memoizeFocusedSpec() {
      if (specName) {
        return;
      }

      var paramMap = [];
      var params = doc.location.search.substring(1).split('&');

      for (var i = 0; i < params.length; i++) {
        var p = params[i].split('=');
        paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
      }

      specName = paramMap.spec;
    })();

    return specName;
  }

  function createReporterDom(version) {
    dom.reporter = self.createDom('div', { id: 'HTMLReporter', className: 'jasmine_reporter' },
      dom.banner = self.createDom('div', { className: 'banner' },
        self.createDom('span', { className: 'title' }, "Jasmine "),
        self.createDom('span', { className: 'version' }, version)),

      dom.symbolSummary = self.createDom('ul', {className: 'symbolSummary'}),
      dom.alert = self.createDom('div', {className: 'alert'}),
      dom.results = self.createDom('div', {className: 'results'},
        dom.summary = self.createDom('div', { className: 'summary' }),
        dom.details = self.createDom('div', { id: 'details' }))
    );
  }
};
jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter);jasmine.HtmlReporterHelpers = {};

jasmine.HtmlReporterHelpers.createDom = function(type, attrs, childrenVarArgs) {
  var el = document.createElement(type);

  for (var i = 2; i < arguments.length; i++) {
    var child = arguments[i];

    if (typeof child === 'string') {
      el.appendChild(document.createTextNode(child));
    } else {
      if (child) {
        el.appendChild(child);
      }
    }
  }

  for (var attr in attrs) {
    if (attr == "className") {
      el[attr] = attrs[attr];
    } else {
      el.setAttribute(attr, attrs[attr]);
    }
  }

  return el;
};

jasmine.HtmlReporterHelpers.getSpecStatus = function(child) {
  var results = child.results();
  var status = results.passed() ? 'passed' : 'failed';
  if (results.skipped) {
    status = 'skipped';
  }

  return status;
};

jasmine.HtmlReporterHelpers.appendToSummary = function(child, childElement) {
  var parentDiv = this.dom.summary;
  var parentSuite = (typeof child.parentSuite == 'undefined') ? 'suite' : 'parentSuite';
  var parent = child[parentSuite];

  if (parent) {
    if (typeof this.views.suites[parent.id] == 'undefined') {
      this.views.suites[parent.id] = new jasmine.HtmlReporter.SuiteView(parent, this.dom, this.views);
    }
    parentDiv = this.views.suites[parent.id].element;
  }

  parentDiv.appendChild(childElement);
};

jasmine.HtmlReporterHelpers.addHelpers = function(ctor) {
  for(var fn in jasmine.HtmlReporterHelpers) {
    ctor.prototype[fn] = jasmine.HtmlReporterHelpers[fn];
  }
};

jasmine.HtmlReporter.ReporterView = function(dom) {
  this.startedAt = new Date();
  this.runningSpecCount = 0;
  this.completeSpecCount = 0;
  this.passedCount = 0;
  this.failedCount = 0;
  this.skippedCount = 0;

  this.createResultsMenu = function() {
    this.resultsMenu = this.createDom('span', {className: 'resultsMenu bar'},
      this.summaryMenuItem = this.createDom('a', {className: 'summaryMenuItem', href: "#"}, '0 specs'),
      ' | ',
      this.detailsMenuItem = this.createDom('a', {className: 'detailsMenuItem', href: "#"}, '0 failing'));

    this.summaryMenuItem.onclick = function() {
      dom.reporter.className = dom.reporter.className.replace(/ showDetails/g, '');
    };

    this.detailsMenuItem.onclick = function() {
      showDetails();
    };
  };

  this.addSpecs = function(specs, specFilter) {
    this.totalSpecCount = specs.length;

    this.views = {
      specs: {},
      suites: {}
    };

    for (var i = 0; i < specs.length; i++) {
      var spec = specs[i];
      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom, this.views);
      if (specFilter(spec)) {
        this.runningSpecCount++;
      }
    }
  };

  this.specComplete = function(spec) {
    this.completeSpecCount++;

    if (isUndefined(this.views.specs[spec.id])) {
      this.views.specs[spec.id] = new jasmine.HtmlReporter.SpecView(spec, dom);
    }

    var specView = this.views.specs[spec.id];

    switch (specView.status()) {
      case 'passed':
        this.passedCount++;
        break;

      case 'failed':
        this.failedCount++;
        break;

      case 'skipped':
        this.skippedCount++;
        break;
    }

    specView.refresh();
    this.refresh();
  };

  this.suiteComplete = function(suite) {
    var suiteView = this.views.suites[suite.id];
    if (isUndefined(suiteView)) {
      return;
    }
    suiteView.refresh();
  };

  this.refresh = function() {

    if (isUndefined(this.resultsMenu)) {
      this.createResultsMenu();
    }

    // currently running UI
    if (isUndefined(this.runningAlert)) {
      this.runningAlert = this.createDom('a', {href: "?", className: "runningAlert bar"});
      dom.alert.appendChild(this.runningAlert);
    }
    this.runningAlert.innerHTML = "Running " + this.completeSpecCount + " of " + specPluralizedFor(this.totalSpecCount);

    // skipped specs UI
    if (isUndefined(this.skippedAlert)) {
      this.skippedAlert = this.createDom('a', {href: "?", className: "skippedAlert bar"});
    }

    this.skippedAlert.innerHTML = "Skipping " + this.skippedCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";

    if (this.skippedCount === 1 && isDefined(dom.alert)) {
      dom.alert.appendChild(this.skippedAlert);
    }

    // passing specs UI
    if (isUndefined(this.passedAlert)) {
      this.passedAlert = this.createDom('span', {href: "?", className: "passingAlert bar"});
    }
    this.passedAlert.innerHTML = "Passing " + specPluralizedFor(this.passedCount);

    // failing specs UI
    if (isUndefined(this.failedAlert)) {
      this.failedAlert = this.createDom('span', {href: "?", className: "failingAlert bar"});
    }
    this.failedAlert.innerHTML = "Failing " + specPluralizedFor(this.failedCount);

    if (this.failedCount === 1 && isDefined(dom.alert)) {
      dom.alert.appendChild(this.failedAlert);
      dom.alert.appendChild(this.resultsMenu);
    }

    // summary info
    this.summaryMenuItem.innerHTML = "" + specPluralizedFor(this.runningSpecCount);
    this.detailsMenuItem.innerHTML = "" + this.failedCount + " failing";
  };

  this.complete = function() {
    dom.alert.removeChild(this.runningAlert);

    this.skippedAlert.innerHTML = "Ran " + this.runningSpecCount + " of " + specPluralizedFor(this.totalSpecCount) + " - run all";

    if (this.failedCount === 0) {
      dom.alert.appendChild(this.createDom('span', {className: 'passingAlert bar'}, "Passing " + specPluralizedFor(this.passedCount)));
    } else {
      showDetails();
    }

    dom.banner.appendChild(this.createDom('span', {className: 'duration'}, "finished in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s"));
  };

  return this;

  function showDetails() {
    if (dom.reporter.className.search(/showDetails/) === -1) {
      dom.reporter.className += " showDetails";
    }
  }

  function isUndefined(obj) {
    return typeof obj === 'undefined';
  }

  function isDefined(obj) {
    return !isUndefined(obj);
  }

  function specPluralizedFor(count) {
    var str = count + " spec";
    if (count > 1) {
      str += "s"
    }
    return str;
  }

};

jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.ReporterView);

jasmine.HtmlReporter.SpecView = function(spec, dom, views) {
  this.spec = spec;
  this.dom = dom;
  this.views = views;

  this.symbol = this.createDom('li', { className: 'pending' });
  this.dom.symbolSummary.appendChild(this.symbol);

  this.summary = this.createDom('div', { className: 'specSummary' },
      this.createDom('a', {
        className: 'description',
        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
        title: this.spec.getFullName()
      }, this.spec.description)
  );

  this.detail = this.createDom('div', { className: 'specDetail' },
      this.createDom('a', {
        className: 'description',
        href: '?spec=' + encodeURIComponent(this.spec.getFullName()),
        title: this.spec.getFullName()
      }, this.spec.getFullName())
  );
};

jasmine.HtmlReporter.SpecView.prototype.status = function() {
  return this.getSpecStatus(this.spec);
};

jasmine.HtmlReporter.SpecView.prototype.refresh = function() {
  this.symbol.className = this.status();

  switch (this.status()) {
    case 'skipped':
      break;

    case 'passed':
      this.appendSummaryToSuiteDiv();
      break;

    case 'failed':
      this.appendSummaryToSuiteDiv();
      this.appendFailureDetail();
      break;
  }
};

jasmine.HtmlReporter.SpecView.prototype.appendSummaryToSuiteDiv = function() {
  this.summary.className += ' ' + this.status();
  this.appendToSummary(this.spec, this.summary);
};

jasmine.HtmlReporter.SpecView.prototype.appendFailureDetail = function() {
  this.detail.className += ' ' + this.status();

  var resultItems = this.spec.results().getItems();
  var messagesDiv = this.createDom('div', { className: 'messages' });

  for (var i = 0; i < resultItems.length; i++) {
    var result = resultItems[i];

    if (result.type == 'log') {
      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
    } else if (result.type == 'expect' && result.passed && !result.passed()) {
      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));

      if (result.trace.stack) {
        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
      }
    }
  }

  if (messagesDiv.childNodes.length > 0) {
    this.detail.appendChild(messagesDiv);
    this.dom.details.appendChild(this.detail);
  }
};

jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SpecView);jasmine.HtmlReporter.SuiteView = function(suite, dom, views) {
  this.suite = suite;
  this.dom = dom;
  this.views = views;

  this.element = this.createDom('div', { className: 'suite' },
      this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(this.suite.getFullName()) }, this.suite.description)
  );

  this.appendToSummary(this.suite, this.element);
};

jasmine.HtmlReporter.SuiteView.prototype.status = function() {
  return this.getSpecStatus(this.suite);
};

jasmine.HtmlReporter.SuiteView.prototype.refresh = function() {
  this.element.className += " " + this.status();
};

jasmine.HtmlReporterHelpers.addHelpers(jasmine.HtmlReporter.SuiteView);

/* @deprecated Use jasmine.HtmlReporter instead
 */
jasmine.TrivialReporter = function(doc) {
  this.document = doc || document;
  this.suiteDivs = {};
  this.logRunningSpecs = false;
};

jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
  var el = document.createElement(type);

  for (var i = 2; i < arguments.length; i++) {
    var child = arguments[i];

    if (typeof child === 'string') {
      el.appendChild(document.createTextNode(child));
    } else {
      if (child) { el.appendChild(child); }
    }
  }

  for (var attr in attrs) {
    if (attr == "className") {
      el[attr] = attrs[attr];
    } else {
      el.setAttribute(attr, attrs[attr]);
    }
  }

  return el;
};

jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
  var showPassed, showSkipped;

  this.outerDiv = this.createDom('div', { id: 'TrivialReporter', className: 'jasmine_reporter' },
      this.createDom('div', { className: 'banner' },
        this.createDom('div', { className: 'logo' },
            this.createDom('span', { className: 'title' }, "Jasmine"),
            this.createDom('span', { className: 'version' }, runner.env.versionString())),
        this.createDom('div', { className: 'options' },
            "Show ",
            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
            )
          ),

      this.runnerDiv = this.createDom('div', { className: 'runner running' },
          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
      );

  this.document.body.appendChild(this.outerDiv);

  var suites = runner.suites();
  for (var i = 0; i < suites.length; i++) {
    var suite = suites[i];
    var suiteDiv = this.createDom('div', { className: 'suite' },
        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
    this.suiteDivs[suite.id] = suiteDiv;
    var parentDiv = this.outerDiv;
    if (suite.parentSuite) {
      parentDiv = this.suiteDivs[suite.parentSuite.id];
    }
    parentDiv.appendChild(suiteDiv);
  }

  this.startedAt = new Date();

  var self = this;
  showPassed.onclick = function(evt) {
    if (showPassed.checked) {
      self.outerDiv.className += ' show-passed';
    } else {
      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
    }
  };

  showSkipped.onclick = function(evt) {
    if (showSkipped.checked) {
      self.outerDiv.className += ' show-skipped';
    } else {
      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
    }
  };
};

jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
  var results = runner.results();
  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
  this.runnerDiv.setAttribute("class", className);
  //do it twice for IE
  this.runnerDiv.setAttribute("className", className);
  var specs = runner.specs();
  var specCount = 0;
  for (var i = 0; i < specs.length; i++) {
    if (this.specFilter(specs[i])) {
      specCount++;
    }
  }
  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);

  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
};

jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
  var results = suite.results();
  var status = results.passed() ? 'passed' : 'failed';
  if (results.totalCount === 0) { // todo: change this to check results.skipped
    status = 'skipped';
  }
  this.suiteDivs[suite.id].className += " " + status;
};

jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
  if (this.logRunningSpecs) {
    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
  }
};

jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
  var results = spec.results();
  var status = results.passed() ? 'passed' : 'failed';
  if (results.skipped) {
    status = 'skipped';
  }
  var specDiv = this.createDom('div', { className: 'spec '  + status },
      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
      this.createDom('a', {
        className: 'description',
        href: '?spec=' + encodeURIComponent(spec.getFullName()),
        title: spec.getFullName()
      }, spec.description));

  var resultItems = results.getItems();
  var messagesDiv = this.createDom('div', { className: 'messages' });
  for (var i = 0; i < resultItems.length; i++) {
    var result = resultItems[i];

    if (result.type == 'log') {
      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
    } else if (result.type == 'expect' && result.passed && !result.passed()) {
      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));

      if (result.trace.stack) {
        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
      }
    }
  }

  if (messagesDiv.childNodes.length > 0) {
    specDiv.appendChild(messagesDiv);
  }

  this.suiteDivs[spec.suite.id].appendChild(specDiv);
};

jasmine.TrivialReporter.prototype.log = function() {
  var console = jasmine.getGlobal().console;
  if (console && console.log) {
    if (console.log.apply) {
      console.log.apply(console, arguments);
    } else {
      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
    }
  }
};

jasmine.TrivialReporter.prototype.getLocation = function() {
  return this.document.location;
};

jasmine.TrivialReporter.prototype.specFilter = function(spec) {
  var paramMap = {};
  var params = this.getLocation().search.substring(1).split('&');
  for (var i = 0; i < params.length; i++) {
    var p = params[i].split('=');
    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
  }

  if (!paramMap.spec) {
    return true;
  }
  return spec.getFullName().indexOf(paramMap.spec) === 0;
};


* Displays a browser dialog where the user can choose a folder to load.

* (If the user cancels the dialog, nothing more happens).

NativeFileSystem.showOpenDialog(false, true, "Choose a folder", null, null,

keep keep keep keep replace keep keep keep

// 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

function Player() {
}
Player.prototype.play = function(song) {
  this.currentlyPlayingSong = song;
  this.isPlaying = true;
};

Player.prototype.pause = function() {
  this.isPlaying = false;
};

Player.prototype.resume = function() {
  if (this.isPlaying) {
    throw new Error("song is already playing");
  }

  this.isPlaying = true;
};

Player.prototype.makeFavorite = function() {
  this.currentlyPlayingSong.persistFavoriteStatus(true);
};


// If length == 0, user canceled the dialog; length should never be > 1

// Actually close all the old files now that we know for sure we're proceeding

// Load the new project into the folder tree

/*
 * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
 */
define(function(require, exports, module) {
    require("thirdparty/path-utils/path-utils.min");

    // Load dependent modules
    var CommandManager      = require("CommandManager")
    ,   Commands            = require("Commands")
    ,   NativeFileSystem    = require("NativeFileSystem").NativeFileSystem
    ,   ProjectManager      = require("ProjectManager")
    ,   DocumentManager     = require("DocumentManager")
    ,   EditorManager       = require("EditorManager")
    ,   Strings             = require("strings");
    ;

    /**
     * Handlers for commands related to file handling (opening, saving, etc.)
     */

    /** @type {jQueryObject} */
    var _title;
    /** @type {string} */
    var _currentFilePath;  // TODO: eliminate this and just use getCurrentDocument().file.fullPath
    /** @type {string} */
    var _currentTitlePath;

    function init(title) {
        _title = title;

        // Register global commands
        CommandManager.register(Commands.FILE_OPEN, handleFileOpen);
        CommandManager.register(Commands.FILE_ADD_TO_WORKING_SET, handleFileAddToWorkingSet);
        // TODO: For now, hook up File > New to the "new in project" handler. Eventually
        // File > New should open a new blank tab, and handleFileNewInProject should
        // be called from a "+" button in the project
        CommandManager.register(Commands.FILE_NEW, handleFileNewInProject);
        CommandManager.register(Commands.FILE_SAVE, handleFileSave);
        CommandManager.register(Commands.FILE_CLOSE, handleFileClose);
        CommandManager.register(Commands.FILE_CLOSE_ALL, handleFileCloseAll);

        $(DocumentManager).on("dirtyFlagChange", handleDirtyChange);
        $(DocumentManager).on("currentDocumentChange", handleCurrentDocumentChange);
    };

    function handleCurrentDocumentChange(event) {
        var newDocument = DocumentManager.getCurrentDocument();

        if (newDocument != null) {
            var fullPath = newDocument.file.fullPath;

            _currentFilePath = _currentTitlePath = fullPath;

            // In the main toolbar, show the project-relative path (if the file is inside the current project)
            // or the full absolute path (if it's not in the project).
            _currentTitlePath = ProjectManager.makeProjectRelativeIfPossible(fullPath);

        } else {
            _currentFilePath = _currentTitlePath = null;
        }

        // Update title text & "dirty dot" display
        updateTitle();
    }

    function handleDirtyChange(event, changedDoc) {
        if (changedDoc.file.fullPath == _currentFilePath) {
            updateTitle();
        }
    }

    function updateTitle() {
        var currentDoc = DocumentManager.getCurrentDocument();
        if (currentDoc) {
            _title.text( _currentTitlePath + (currentDoc.isDirty ? " \u2022" : "") );
        } else {
            _title.text("");
        }
    }

    function handleFileAddToWorkingSet(fullPath){
        handleFileOpen(fullPath);
        DocumentManager.addToWorkingSet(DocumentManager.getCurrentDocument());
    }

    function handleFileOpen(fullPath) {
        var result = doOpenWithOptionalPath(fullPath);
        result.always(function() {
            EditorManager.focusEditor();
        });
        return result;
    }

    function doOpenWithOptionalPath(fullPath) {
        var result;
        if (!fullPath) {
            // Prompt the user with a dialog
            // TODO: we're relying on this to not be asynchronous--is that safe?
            NativeFileSystem.showOpenDialog(false, false, Strings.OPEN_FILE, ProjectManager.getProjectRoot().fullPath,
                ["htm", "html", "js", "css"], function(files) {
                    if (files.length > 0) {
                        result = doOpen(files[0]);
                        return;
                    }
                });
        }
        else {
            result = doOpen(fullPath);
        }
        if (!result)
            result = (new $.Deferred()).reject();
        return result;
    }

    function doOpen(fullPath) {
        var result = new $.Deferred();
        if (!fullPath) {
            console.log("doOpen() called without fullPath");
            return result.reject();
        }

        // TODO: we should implement something like NativeFileSystem.resolveNativeFileSystemURL() (similar
        // to what's in the standard file API) to get a FileEntry, rather than manually constructing it
        var fileEntry = new NativeFileSystem.FileEntry(fullPath);

        var document = DocumentManager.getDocumentForFile(fileEntry);
        if (document != null) {
            // File already open - don't need to load it, just switch to it in the UI
            DocumentManager.showInEditor(document);
            result.resolve();

        } else {
            // File wasn't open before, so we must load its contents into a new document
            var reader = new NativeFileSystem.FileReader();

            fileEntry.file(function(file) {
                reader.onload = function(event) {
                    // Create a new editor initialized with the file's content, and bind it to a Document
                    document = EditorManager.createDocumentAndEditor(fileEntry, event.target.result);

                    // Switch to new document in the UI
                    DocumentManager.showInEditor(document);
                    result.resolve();
                };

                reader.onerror = function(event) {
                    showFileOpenError(event.target.error.code, fullPath);
                    result.reject();
                }

                reader.readAsText(file, "utf8");
            },
            function fileEntry_onerror(event) {
                showFileOpenError(event.target.error.code, fullPath);
                result.reject();
            });
        }

        return result;
    }

    function handleFileNewInProject() {
        // Determine the directory to put the new file
        // If a file is currently selected, put it next to it.
        // If a directory is currently selected, put it in it.
        // If nothing is selected, put it at the root of the project
        var baseDir,
            selected = ProjectManager.getSelectedItem() || ProjectManager.getProjectRoot();

        baseDir = selected.fullPath;
        if (selected.isFile)
            baseDir = baseDir.substr(0, baseDir.lastIndexOf("/"));

        // Create the new node. The createNewItem function does all the heavy work
        // of validating file name, creating the new file and selecting.
        // TODO: Use a unique name like Untitled-1, Untitled-2, etc.
        return ProjectManager.createNewItem(baseDir, "Untitled.js", false);
    }

    function handleFileSave() {
        return doSave( DocumentManager.getCurrentDocument() );
    }
    function doSave(docToSave) {
        var result = new $.Deferred();

        if (docToSave && docToSave.isDirty) {
            var fileEntry = docToSave.file;

            //setup our resolve and reject handlers
            result.done( function fileSaved() {
                docToSave.markClean();
            });

            result.fail( function fileError(error) {
                showSaveFileError(error.code, fileEntry.fullPath);
            });

            fileEntry.createWriter(
                function(writer) {
                    writer.onwriteend = function() {
                        result.resolve();
                    }
                    writer.onerror = function(error) {
                        result.reject(error);
                    }

                    // TODO (jasonsj): Blob instead of string
                    writer.write( docToSave.getText() );
                },
                function(error) {
                    result.reject(error);
                }
            );
        }
        else {
            result.resolve();
        }
        result.always(function() {
            EditorManager.focusEditor();
        });
        return result;
    }

    function saveAll() {
        var saveResults = [];

        DocumentManager.getWorkingSet().forEach(function(doc) {  //TODO: or just use for..in?
            saveResults.push( doSave(doc) );
        });

        // $.when() is sort of crappy: it won't accept an array as an arg, and it seems to insist
        // on receiving Deferreds even though it should work fine given Promises too... ugh
        var overallResult = $.when.apply($, saveResults);

        return overallResult; //NOTE: this returns a Promise, NOT a Deferred (which is actually more correct)
    }

    /** Closes the specified document. Assumes the current document if doc is null.
     * Prompts user about saving file if document is dirty
     * @param {?Document} doc
     */
    function handleFileClose( doc ) {

        // utility function for handleFileClose: closes document & removes from working set
        function doClose(doc) {
            // This selects a different document if the working set has any other options
            DocumentManager.closeDocument(doc);

            EditorManager.focusEditor();
        }

        var result = new $.Deferred();

        // Default to current document if doc is null
        if (!doc)
            doc =  DocumentManager.getCurrentDocument();
        // No-op if called when nothing is open; TODO: should command be grayed out instead?
        if (!doc)
            return;

        if (doc.isDirty) {
            var filename = PathUtils.parseUrl(doc.file.fullPath).filename;

            brackets.showModalDialog(
                  brackets.DIALOG_ID_SAVE_CLOSE
                , Strings.SAVE_CLOSE_TITLE
                , Strings.format(Strings.SAVE_CLOSE_MESSAGE, filename )
            ).done(function(id) {
                if (id === brackets.DIALOG_BTN_CANCEL) {
                    result.reject();
                }
                else {
                    if (id === brackets.DIALOG_BTN_OK) {
                        doSave(doc)
                            .done(function() {
                                doClose(doc);
                                result.resolve();
                            })
                            .fail(function() {
                                result.reject();
                            });
                    }
                    else {
                        // This is the "Don't Save" case--we can just go ahead and close the file.
                        doClose(doc);
                        result.resolve();
                    }
                }
            });
            result.always(function() {
                EditorManager.focusEditor();
            });
        }
        else {
            // Doc is not dirty, just close
            doClose(doc);
            EditorManager.focusEditor();
            result.resolve();
        }
        return result;
    }

    function handleFileCloseAll() {
        var result = new $.Deferred();

        var unsavedDocs = DocumentManager.getWorkingSet().filter( function(doc) {
            return doc.isDirty;
        } );

        if (unsavedDocs.length == 0) {
            console.log("ZERO UNSAVED...");

            DocumentManager.closeAll();
            result.resolve();

        } else if (unsavedDocs.length == 1) {
            console.log("SINGLE UNSAVED...");

            handleFileClose( unsavedDocs[0] ).done( function() {
                DocumentManager.closeAll();
                result.resolve();
            }).fail( function() {
                result.reject();
            });

        } else {
            console.log("MULTIPLE UNSAVED...");
            var message = Strings.SAVE_CLOSE_MULTI_MESSAGE;

            message += "
";
            unsavedDocs.forEach(function(doc) {  //TODO: or just use for..in?
                message += "
" + ProjectManager.makeProjectRelativeIfPossible(doc.file.fullPath) + "
";
            });
            message += "
";

            brackets.showModalDialog(
                  brackets.DIALOG_ID_SAVE_CLOSE
                , Strings.SAVE_CLOSE_TITLE
                , message
            ).done(function(id) {
                if (id === brackets.DIALOG_BTN_CANCEL) {
                    result.reject();
                }
                else {
                    if (id === brackets.DIALOG_BTN_OK) {
                        console.log("SAVE ALL, then CLOSE ALL");
                        saveAll().done( function() {
                            DocumentManager.closeAll();
                            result.resolve();
                        }).fail( function() {
                            result.reject();
                        });
                    }
                    else {
                        // This is the "Don't Save" case--we can just go ahead and close the file.
                        console.log("DISCARD ALL, then CLOSE ALL");
                        DocumentManager.closeAll();
                        result.resolve();
                    }
                }
            });
        }

        return result;
    }

    function showFileOpenError(code, path) {
        brackets.showModalDialog(
              brackets.DIALOG_ID_ERROR
            , Strings.ERROR_OPENING_FILE_TITLE
            , Strings.format(
                    Strings.ERROR_OPENING_FILE
                  , path
                  , getErrorString(code))
        );
    }

    function showSaveFileError(code, path) {
        brackets.showModalDialog(
              brackets.DIALOG_ID_ERROR
            , Strings.ERROR_SAVING_FILE_TITLE
            , Strings.format(
                    Strings.ERROR_SAVING_FILE
                  , path
                  , getErrorString(code))
        );
    }

    function getErrorString(code) {
        // There are a few error codes that we have specific error messages for. The rest are
        // displayed with a generic "(error N)" message.
        var result;

        if (code == FileError.NOT_FOUND_ERR)
            result = Strings.NOT_FOUND_ERR;
        else if (code == FileError.NOT_READABLE_ERR)
            result = Strings.NOT_READABLE_ERR;
        else if (code == FileError.NO_MODIFICATION_ALLOWED_ERR)
            result = Strings.NO_MODIFICATION_ALLOWED_ERR_FILE;
        else
            result = Strings.format(Strings.GENERIC_ERROR, code);

        return result;
    }

    // Define public API
    exports.init = init;
});


/* Brackets Variables */

/* All paddings, gutters, etc. should be multiples of this */
@base-padding: 10px;


/*
 * jsTree classic theme 1.0
 * Supported features: dots/no-dots, icons/no-icons, focused, loading
 * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
 */

.jstree-classic li,
.jstree-classic ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
.jstree-classic li { background-position:-90px 0; background-repeat:repeat-y;  }
.jstree-classic li.jstree-last { background:transparent; }
.jstree-classic .jstree-open > ins { background-position:-72px 0; }
.jstree-classic .jstree-closed > ins { background-position:-54px 0; }
.jstree-classic .jstree-leaf > ins { background-position:-36px 0; }

.jstree-classic .jstree-hovered { background:#e7f4f9; border:1px solid #e7f4f9; padding:0 2px 0 1px; }
.jstree-classic .jstree-clicked { background:navy; border:1px solid navy; padding:0 2px 0 1px; color:white; }
.jstree-classic a .jstree-icon { background-position:-56px -19px; }
.jstree-classic .jstree-open > a .jstree-icon { background-position:-56px -36px; }
.jstree-classic a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }

.jstree-classic.jstree-focused { background:white; }

.jstree-classic .jstree-no-dots li,
.jstree-classic .jstree-no-dots .jstree-leaf > ins { background:transparent; }
.jstree-classic .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
.jstree-classic .jstree-no-dots .jstree-closed > ins { background-position:0 0; }

.jstree-classic .jstree-no-icons a .jstree-icon { display:none; }

.jstree-classic .jstree-search { font-style:italic; }

.jstree-classic .jstree-no-icons .jstree-checkbox { display:inline-block; }
.jstree-classic .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
.jstree-classic .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
.jstree-classic .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
.jstree-classic .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
.jstree-classic .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
.jstree-classic .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
.jstree-classic .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }

#vakata-dragged.jstree-classic ins { background:transparent !important; }
#vakata-dragged.jstree-classic .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
#vakata-dragged.jstree-classic .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
#jstree-marker.jstree-classic { background:url("d.png") -41px -57px no-repeat !important; text-indent:-100px; }

.jstree-classic a.jstree-search { color:aqua; }
.jstree-classic .jstree-locked a { color:silver; cursor:default; }

#vakata-contextmenu.jstree-classic-context,
#vakata-contextmenu.jstree-classic-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
#vakata-contextmenu.jstree-classic-context li { }
#vakata-contextmenu.jstree-classic-context a { color:black; }
#vakata-contextmenu.jstree-classic-context a:hover,
#vakata-contextmenu.jstree-classic-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
#vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a,
#vakata-contextmenu.jstree-classic-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
#vakata-contextmenu.jstree-classic-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
#vakata-contextmenu.jstree-classic-context li ul { margin-left:-4px; }

/* IE6 BEGIN */
.jstree-classic li,
.jstree-classic ins,
#vakata-dragged.jstree-classic .jstree-invalid,
#vakata-dragged.jstree-classic .jstree-ok,
#jstree-marker.jstree-classic { _background-image:url("d.gif"); }
.jstree-classic .jstree-open ins { _background-position:-72px 0; }
.jstree-classic .jstree-closed ins { _background-position:-54px 0; }
.jstree-classic .jstree-leaf ins { _background-position:-36px 0; }
.jstree-classic .jstree-open a ins.jstree-icon { _background-position:-56px -36px; }
.jstree-classic .jstree-closed a ins.jstree-icon { _background-position:-56px -19px; }
.jstree-classic .jstree-leaf a ins.jstree-icon { _background-position:-56px -19px; }
#vakata-contextmenu.jstree-classic-context ins { _display:none; }
#vakata-contextmenu.jstree-classic-context li { _zoom:1; }
.jstree-classic .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; }
.jstree-classic .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; }
.jstree-classic .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; }
/* IE6 END */


[submodule "src/thirdparty/CodeMirror2"]
	path = src/thirdparty/CodeMirror2
	url = git@github.com:adobe/CodeMirror2.git
[submodule "src/thirdparty/path-utils"]
	path = src/thirdparty/path-utils
	url = git@github.com:jblas/path-utils.git


/*
 * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
 */

// TODO: break out the definition of brackets into a separate module from the application controller logic
define(function(require, exports, module) {
    // Load dependent non-module scripts
    require("widgets/bootstrap-dropdown");
    require("widgets/bootstrap-modal");

    // Load dependent modules
    var PreferencesManager      = require("PreferencesManager")
    ,   ProjectManager          = require("ProjectManager")
    ,   DocumentManager         = require("DocumentManager")
    ,   EditorManager           = require("EditorManager")
    ,   WorkingSetView          = require("WorkingSetView")
    ,   FileCommandHandlers     = require("FileCommandHandlers")
    ,   KeyBindingManager       = require("KeyBindingManager").KeyBindingManager
    ,   KeyMap                  = require("KeyBindingManager").KeyMap
    ,   Commands                = require("Commands")
    ;

    // Define core brackets namespace
    brackets = window.brackets || {};

    // TODO: 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 =
        { PreferencesManager    : PreferencesManager
        , ProjectManager        : ProjectManager
        , FileCommandHandlers   : FileCommandHandlers
        , DocumentManager       : DocumentManager
        , Commands              : Commands
        , WorkingSetView        : WorkingSetView
        , CommandManager        : require("CommandManager")
        };

    brackets.inBrowser = !brackets.hasOwnProperty("fs");

    brackets.DIALOG_BTN_CANCEL = "cancel";
    brackets.DIALOG_BTN_OK = "ok";
    brackets.DIALOG_BTN_DONTSAVE = "dontsave";

    brackets.DIALOG_ID_ERROR = "error-dialog";
    brackets.DIALOG_ID_SAVE_CLOSE = "save-close-dialog";

    /**
     * General purpose modal dialog. Assumes that the HTML for the dialog contains elements with "title"
     * and "message" classes, as well as a number of elements with "dialog-button" class, each of which has
     * a "data-button-id".
     *
     * @param {string} id The ID of the dialog node in the HTML.
     * @param {string} title The title of the error dialog. Can contain HTML markup.
     * @param {string} message The message to display in the error dialog. Can contain HTML markup.
     * @return {Deferred} a $.Deferred() that will be resolved with the ID of the clicked button when the dialog
     *     is dismissed. Never rejected.
     */
    brackets.showModalDialog = function(id, title, message, callback) {
        var result = $.Deferred();
        var dlg = $("#" + id);

        // Set title and message
        $(".dialog-title", dlg).html(title);
        $(".dialog-message", dlg).html(message);

        function dismissDialog(buttonId) {
            dlg.one("hidden", function() {
                result.resolve(buttonId);
            });
            dlg.modal(true).hide();
        }
        // Click handler for buttons
        dlg.one("click", ".dialog-button", function(e) {
            dismissDialog($(this).attr("data-button-id"));
        });

        // Enter/Return handler for the primary button. Need to
        // add both keydown and keyup handlers here to make sure
        // the enter key was pressed while the dialog was showing.
        // Otherwise, if a keydown or keypress from somewhere else
        // triggered an alert, the keyup could immediately dismiss it.
        var enterKeyPressed = false;
        $(document).on("keydown.modal", function(e) {
            if (e.keyCode === 13) {
                enterKeyPressed = true;
            }
        }).on("keyup.modal", function(e) {
            if (e.keyCode === 13 && enterKeyPressed) {
                var primaryBtn = dlg.find(".primary");
                if (primaryBtn) {
                    dismissDialog(primaryBtn.attr("data-button-id"));
                }
            }
            enterKeyPressed = false;
        });

        // Run the dialog
        dlg.modal(
            { backdrop: "static"
            , show: true
            }
        ).on("hide", function(e) {
            // Remove all handlers in the .modal namespace
            $(document).off(".modal");
        });
        return result;
    };

    $(document).ready(function() {

        EditorManager.setEditorHolder( $('#editorHolder') );

        initProject();
        initMenus();
        initCommandHandlers();
        initKeyBindings();

        function initProject() {
            ProjectManager.loadProject();

            // Open project button
            $("#btn-open-project").click(function() {
                ProjectManager.openProject();
            });

            // Handle toggling top level disclosure arrows of file list area
            $("#open-files-disclosure-arrow").click(function(){
                $(this).toggleClass( "disclosure-arrow-closed");
                $("#open-files-container").toggle();
            });
            $("#project-files-disclosure-arrow").click(function(){
                $(this).toggleClass( "disclosure-arrow-closed");
                $("#project-files-container").toggle();
            });

        }

        function initMenus() {
            // Implements the File menu items
            $("#menu-file-new").click(function() {
                CommandManager.execute(Commands.FILE_NEW);
            });
            $("#menu-file-open").click(function() {
                CommandManager.execute(Commands.FILE_OPEN);
            });
            $("#menu-file-close").click(function() {
                CommandManager.execute(Commands.FILE_CLOSE);
            });
            $("#menu-file-save").click(function() {
                CommandManager.execute(Commands.FILE_SAVE);
            });
            $("#menu-file-quit").click(function() {
                doQuit();
            });

            // Implements the 'Run Tests' menu to bring up the Jasmine unit test window
            var testWindow = null;
            $("#menu-debug-runtests").click(function(){
                if (!(testWindow === null)) {
                    try {
                        testWindow.location.reload();
                    } catch(e) {
                        testWindow = null;  // the window was probably closed
                    }
                }

                if (testWindow === null) {
                    testWindow = window.open("../test/SpecRunner.html");
                    testWindow.location.reload(); // if it was opened before, we need to reload because it will be cached
                }
            });

            // Other debug menu items
//            $("#menu-debug-wordwrap").click(function() {
//                editor.setOption("lineWrapping", !(editor.getOption("lineWrapping")));
//            });
        }

        function initCommandHandlers() {
            FileCommandHandlers.init( $("#main-toolbar .title") );
        }

        function initKeyBindings() {
            // Register keymaps and install the keyboard handler
            // TODO: show keyboard equivalents in the menus
            var _globalKeymap = new KeyMap(
                { "Ctrl-O": Commands.FILE_OPEN
                , "Ctrl-S": Commands.FILE_SAVE
                , "Ctrl-W": Commands.FILE_CLOSE
                }
            );
            KeyBindingManager.installKeymap(_globalKeymap);

            $(document.body).keydown(function(event) {
                var keyDescriptor = [];
                if (event.metaKey || event.ctrlKey) {
                    keyDescriptor.push("Ctrl");
                }
                if (event.altKey) {
                    keyDescriptor.push("Alt");
                }
                if (event.shiftKey) {
                    keyDescriptor.push("Shift");
                }
                keyDescriptor.push(String.fromCharCode(event.keyCode).toUpperCase());
                if (KeyBindingManager.handleKey(keyDescriptor.join("-"))) {
                    event.preventDefault();
                }
            });
        }
    });

    function doQuit() {
        var closeAllResult = CommandManager.execute(Commands.FILE_CLOSE_ALL);

        closeAllResult
        .done(function() {
            //window.close();
            console.log("TODO: window.close()!");
        })
        .fail(function() {
            // don't exit: user canceled (or asked us to save changes first, but we failed to do so)
        });
    }

    $(window).unload(function () {
        PreferencesManager.savePreferences();
    });
});


/*
 * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
 */

define(function(require, exports, module) {

    /**
     * Format a string by replacing placeholder symbols with passed in arguments.
     *
     * Example: var formatted = Strings.format("Hello {0}", "World");
     *
     * @param {string} str The base string
     * @param {...} Arguments to be substituted into the string
     *
     * @return {string} Formatted string
     */
    function format(str) {
        // arguments[0] is the base string, so we need to adjust index values here
        var args = [].slice.call(arguments, 1);
        return str.replace(/{(\d+)}/g, function(match, num) {
            return typeof args[num] !== 'undefined' ? args[num] : match;
        });
    }

    // Define public API
    exports.format                            = format;

    // General file io error strings
    exports.GENERIC_ERROR                     = "(error {0})";
    exports.NOT_FOUND_ERR                     = "The file could not be found.";
    exports.NOT_READABLE_ERR                  = "The file could not be read.";
    exports.NO_MODIFICATION_ALLOWED_ERR       = "The target directory cannot be modified.";
    exports.NO_MODIFICATION_ALLOWED_ERR_FILE  = "The permissions do not allow you to make modifications.";

    // Project error strings
    exports.ERROR_LOADING_PROJECT             = "Error loading project";
    exports.OPEN_DIALOG_ERROR                 = "An error occurred when showing the open file dialog. (error {0})";
    exports.REQUEST_NATIVE_FILE_SYSTEM_ERROR  = "An error occurred when trying to load the directory \"{0}\". (error {1})";
    exports.READ_DIRECTORY_ENTRIES_ERROR      = "An error occurred when reading the contents of the directory \"{0}\". (error {1})";

    // File open/save error string
    exports.ERROR_OPENING_FILE_TITLE          = "Error opening file";
    exports.ERROR_OPENING_FILE                = "An error occurred when trying to open the file \"{0}\". {1}";
    exports.ERROR_SAVING_FILE_TITLE           = "Error saving file";
    exports.ERROR_SAVING_FILE                 = "An error occurred when trying to save the file \"{0}\". {1}";
    exports.INVALID_FILENAME_TITLE            = "Invalid file name";
    exports.INVALID_FILENAME_MESSAGE          = "Filenames cannot contain the following characters: /?*:;{}<>\\|";
    exports.FILE_ALREADY_EXISTS               = "The file \"{0}\" already exists.";
    exports.ERROR_CREATING_FILE_TITLE         = "Error creating file";
    exports.ERROR_CREATING_FILE               = "An error occurred when trying to create the file \"{0}\". {1}"

    exports.SAVE_CLOSE_TITLE                  = "Save Changes";
    exports.SAVE_CLOSE_MESSAGE                = "Do you want to save the changes you made in the document \"{0}\"?";
    exports.SAVE_CLOSE_MULTI_MESSAGE          = "Do you want to save your changes to the following files?";

    exports.OPEN_FILE                         = "Open File";
});


/* =============================================================
 * bootstrap-scrollspy.js v1.4.0
 * http://twitter.github.com/bootstrap/javascript.html#scrollspy
 * =============================================================
 * Copyright 2011 Twitter, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============================================================== */

!function ( $ ) {

  "use strict"

  var $window = $(window)

  function ScrollSpy( topbar, selector ) {
    var processScroll = $.proxy(this.processScroll, this)
    this.$topbar = $(topbar)
    this.selector = selector || 'li > a'
    this.refresh()
    this.$topbar.delegate(this.selector, 'click', processScroll)
    $window.scroll(processScroll)
    this.processScroll()
  }

  ScrollSpy.prototype = {

      refresh: function () {
        this.targets = this.$topbar.find(this.selector).map(function () {
          var href = $(this).attr('href')
          return /^#\w/.test(href) && $(href).length ? href : null
        })

        this.offsets = $.map(this.targets, function (id) {
          return $(id).offset().top
        })
      }

    , processScroll: function () {
        var scrollTop = $window.scrollTop() + 10
          , offsets = this.offsets
          , targets = this.targets
          , activeTarget = this.activeTarget
          , i

        for (i = offsets.length; i--;) {
          activeTarget != targets[i]
            && scrollTop >= offsets[i]
            && (!offsets[i + 1] || scrollTop <= offsets[i + 1])
            && this.activateButton( targets[i] )
        }
      }

    , activateButton: function (target) {
        this.activeTarget = target

        this.$topbar
          .find(this.selector).parent('.active')
          .removeClass('active')

        this.$topbar
          .find(this.selector + '[href="' + target + '"]')
          .parent('li')
          .addClass('active')
      }

  }

  /* SCROLLSPY PLUGIN DEFINITION
   * =========================== */

  $.fn.scrollSpy = function( options ) {
    var scrollspy = this.data('scrollspy')

    if (!scrollspy) {
      return this.each(function () {
        $(this).data('scrollspy', new ScrollSpy( this, options ))
      })
    }

    if ( options === true ) {
      return scrollspy
    }

    if ( typeof options == 'string' ) {
      scrollspy[options]()
    }

    return this
  }

  $(document).ready(function () {
    $('body').scrollSpy('[data-scrollspy] li > a')
  })

}( window.jQuery || window.ender );


define(function(require, exports, module) {

    // Load dependent modules
    var CommandManager      // loaded from brackets.test
    ,   Commands            // loaded from brackets.test
    ,   DocumentManager     // loaded from brackets.test
    ,    ProjectManager        // loaded from brackets.test
    ,   SpecRunnerUtils     = require("./SpecRunnerUtils.js");
    ;

    // FIXME (jasonsj): these tests are ommitted when launching in the main app window
    if (window.opener) {

        beforeEach(function() {
    //        this.app = window.open(SpecRunnerUtils.getBracketsSourceRoot() + "/index.html");
            // TODO: this will only work if run from the main Brackets window (not from jasmine.sh)
            this.app = window.opener;

            // Load module instances from brackets.test
            CommandManager      = this.app.brackets.test.CommandManager;
            Commands            = this.app.brackets.test.Commands;
            DocumentManager     = this.app.brackets.test.DocumentManager;
            ProjectManager      = this.app.brackets.test.ProjectManager;

            this.app.location.reload();
            this.testPath = SpecRunnerUtils.getTestPath("/spec/WorkingSetView-test-files/");
            var isReady = false;
            $(this.app.document).ready(function() {
                isReady = true;
            });
            waitsFor(function() { return isReady; }, 5000);

            var didOpen = false, gotError = false;

            // Open a directory
            ProjectManager.loadProject(this.testPath);

            var openAndMakeDirty = function (path){
                // open file
                runs(function() {
                    CommandManager.execute(Commands.FILE_OPEN, path)
                        .done(function() { didOpen = true; })
                        .fail(function() { gotError = true; });
                });
                waitsFor(function() { return didOpen && !gotError; }, "FILE_OPEN on file timeout", 1000);

                // change editor content to make doc dirty
                runs(function() {
                    var editor = DocumentManager.getCurrentDocument()._editor;
                    editor.setValue("dirty document");
                    expect(DocumentManager.getCurrentDocument().isDirty).toBe(true);
                });

            };

            openAndMakeDirty(this.testPath + "/file_one.js");
            openAndMakeDirty(this.testPath + "/file_two.js");

        });

    describe("WorkingSetView", function(){

        it("should add a list item when a file is dirtied", function() {
            // check if files are added to work set and dirty icons are present
            runs(function() {
                var listItems = this.app.$("#open-files-container > ul").children();
                expect( listItems.length ).toBe(2);
                expect( listItems.find("a").get(0).text == "file_one.js" ).toBeTruthy();
                expect( listItems.find(".file-status-icon").length).toBe(2);
            });

        });

        it("should remove a list item when a file is closed", function() {
            DocumentManager.getCurrentDocument().markClean(); // so we can close without a save dialog

            // close the document
            var didClose = false, gotError = false;
            runs(function() {
                CommandManager.execute(Commands.FILE_CLOSE)
                    .done(function() { didClose = true; })
                    .fail(function() { gotError = true; });
            });
            waitsFor(function() { return didClose && !gotError; }, "FILE_OPEN on file timeout", 1000);

            // check there are no list items
            runs(function() {
                var listItems = this.app.$("#open-files-container > ul").children();
                expect( listItems.length ).toBe(1);
            });

        });

        it("should make a file that is clicked the current one in the editor", function() {
            var $ = this.app.$;
            var secondItem =  $($("#open-files-container > ul").children()[1]);
            secondItem.trigger('click');

            var listItems = $("#open-files-container > ul").children();
            expect( $(listItems[0]).hasClass("selected") ).not.toBeTruthy();
            expect( $(listItems[1]).hasClass("selected") ).toBeTruthy();

        });

        // TODO Ty: Can't write this test yet until Jason's persistant work is complete
        // it("should rebuild the ui from the model correctly", function() {
        //
        // });

        it("should close a file when the user clicks the close button", function() {
            var $ = this.app.$;

            // make 2nd doc clean
            var docList = DocumentManager.getWorkingSet();
            docList[1].markClean();

            // make the first one active
            DocumentManager.showInEditor(docList[0]);

            // hover over and click on close icon of 2nd list item
            var secondItem =  $($("#open-files-container > ul").children()[1]);
            secondItem.trigger('mouseover');
            var closeIcon = secondItem.find(".file-status-icon");
            expect(closeIcon.length).toBe(1) ;

            // simulate click
            var didClose = false;
            $(DocumentManager).on("workingSetRemove", function(event, removedDoc) {
                didClose = true;
            });

            closeIcon.trigger('click');
            waitsFor(function() { return didClose; }, "click on working set close icon timeout", 1000);

            var listItems = $("#open-files-container > ul").children();
            expect( listItems.length ).toBe(1);
            expect( listItems.find("a").get(0).text == "file_one.js" ).toBeTruthy();

        });

        it("should remove dirty icon when file becomes clean", function() {
            // check that dirty icon is removed when docs are cleaned
            var docList = DocumentManager.getWorkingSet();
           docList[0].markClean();
           var listItems = this.app.$("#open-files-container > ul").children();
           expect( listItems.find(".file-status-icon dirty").length).toBe(0);

        });

    });
    }
});


keep keep keep replace keep keep keep keep keep

// don't open new project: user canceled (or we failed to save its unsaved changes)

/*
 * Copyright 2011 Adobe Systems Incorporated. All Rights Reserved.
 */
define(function(require, exports, module) {
    // Load dependent non-module scripts
    require("thirdparty/jstree_pre1.0_fix_1/jquery.jstree");

    // Load dependent modules
    var NativeFileSystem    = require("NativeFileSystem").NativeFileSystem
    ,   PreferencesManager  = require("PreferencesManager")
    ,   CommandManager      = require("CommandManager")
    ,   Commands            = require("Commands")
    ,   Strings             = require("strings")
    ;

    /**
     * Returns the root folder of the currently loaded project, or null if no project is open (during
     * startup, or running outside of app shell).
     * @return {DirectoryEntry}
     */
    function getProjectRoot() {
        return _projectRoot;
    }
    /**
     * @private
     * @see getProjectRoot()
     */
    var _projectRoot = null;

    /**
     * Returns true if absPath lies within the project, false otherwise.
     * FIXME: Does not support paths containing ".."
     */
    function isWithinProject(absPath) {
        var rootPath = _projectRoot.fullPath;
        if (rootPath.charAt(rootPath.length - 1) != "/") {  // TODO: standardize whether DirectoryEntry.fullPath can end in "/"
            rootPath += "/";
        }
        return (absPath.indexOf(rootPath) == 0);
    }
    /**
     * If absPath lies within the project, returns a project-relative path. Else returns absPath
     * unmodified.
     * FIXME: Does not support paths containing ".."
     */
    function makeProjectRelativeIfPossible(absPath) {
        if (isWithinProject(absPath)) {
            var relPath = absPath.slice(_projectRoot.fullPath.length);
            if (relPath.charAt(0) == '/') {  // TODO: standardize whether DirectoryEntry.fullPath can end in "/"
                relPath = relPath.slice(1);
            }
            return relPath;
        }
        return absPath;
    }

    /**
     * @private
     * Reference to the tree control
     * @type {jQueryObject}
     */
    var _projectTree = null;

    /**
     * @private
     * Used to initialize jstree state
     */
    var _projectInitialLoad =
        { previous : []
        , id : 0
        , fullPathToIdMap : {}
        };

    /**
     * @private
     * Preferences callback. Saves current project path.
     */
    function savePreferences( storage ) {
        // save the current project
        storage.projectPath = _projectRoot.fullPath;

        // save jstree state
        var openNodes = []
        ,   projectPathLength = _projectRoot.fullPath.length + 1
        ,   entry
        ,   fullPath
        ,   shortPath
        ,   depth;

        // Query open nodes by class selector
        $(".jstree-open").each( function ( index ) {
            entry = $( this ).data("entry");

            if ( entry.fullPath ) {
                fullPath = entry.fullPath;

                // Truncate project path prefix
                shortPath = fullPath.slice( projectPathLength );

                // Determine depth of the node by counting path separators.
                // Children at the root have depth of zero.
                depth = shortPath.split("/").length - 1;

                // Map tree depth to list of open nodes
                if ( openNodes[ depth ] === undefined ) {
                    openNodes[ depth ] = [];
                }

                openNodes[ depth ].push( fullPath );
            }
        });

        // Store the open nodes by their full path and persist to storage
        storage.projectTreeState = openNodes;
    }

    /**
     * Unique PreferencesManager clientID
     */
    var PREFERENCES_CLIENT_ID = "com.adobe.brackets.ProjectManager";

    /**
     * Displays a browser dialog where the user can choose a folder to load.
     * (If the user cancels the dialog, nothing more happens).
     */
    function openProject() {
        if (!brackets.inBrowser) {
            // Pop up a folder browse dialog
            NativeFileSystem.showOpenDialog(false, true, "Choose a folder", null, null,
                function(files) {
                    // If length == 0, user canceled the dialog; length should never be > 1
                    if (files.length > 0)
                        loadProject( files[0] );
                },
                function(error) {
                    brackets.showModalDialog(
                          brackets.DIALOG_ID_ERROR
                        , Strings.ERROR_LOADING_PROJECT
                        , Strings.format(Strings.OPEN_DIALOG_ERROR, error.code)
                    );
                }
            );
        }
    }

    /**
     * Loads the given folder as a project. Normally, you would call openProject() instead to let the
     * user choose a folder.
     *
     * @param {string} rootPath  Absolute path to the root folder of the project.
     */
    function loadProject(rootPath) {
        // reset tree node id's
        _projectInitialLoad.id = 0;

        var prefs = PreferencesManager.getPreferences(PREFERENCES_CLIENT_ID);

        if (rootPath === null || rootPath === undefined) {
            // Load the last known project into the tree
            rootPath = prefs.projectPath;
            _projectInitialLoad.previous = prefs.projectTreeState;

            if (brackets.inBrowser) {
                // In browser: dummy folder tree (hardcoded in ProjectManager)
               rootPath = "DummyProject";
            }
        }

        // Set title
        var projectName = rootPath.substring(rootPath.lastIndexOf("/") + 1);
        $("#project-title").html(projectName);

        // Populate file tree
        if (brackets.inBrowser) {
            // Hardcoded dummy data for local testing, in jsTree JSON format
            // (we leave _projectRoot null)
            var subfolderInner = { data:"Folder_inner", children:[
                { data: "subsubfile_1" }, { data: "subsubfile_2" }
            ] };
            var treeJSONData = [
                { data: "Dummy tree content:" },
                { data:"Folder_1", children:[
                    { data: "subfile_1" }, { data: "subfile_2" }, { data: "subfile_3" }
                ] },
                { data:"Folder_2", children:[
                    { data: "subfile_4" }, subfolderInner, { data: "subfile_5" }
                ] },
                { data: "file_1" },
                { data: "file_2" }
            ];

            // Show file list in UI synchronously
            _renderTree(treeJSONData);

        } else {
            // Point at a real folder structure on local disk
            NativeFileSystem.requestNativeFileSystem(rootPath,
                function(rootEntry) {
                    // Success!
                    _projectRoot = rootEntry;

                    // The tree will invoke our "data provider" function to populate the top-level items, then
                    // go idle until a node is expanded - at which time it'll call us again to fetch the node's
                    // immediate children, and so on.
                    _renderTree(_treeDataProvider);
                },
                function(error) {
                    brackets.showModalDialog(
                          brackets.DIALOG_ID_ERROR
                        , Strings.ERROR_LOADING_PROJECT
                        , Strings.format(Strings.REQUEST_NATIVE_FILE_SYSTEM_ERROR, rootPath, error.code)
                    );
                }
            );
        }
    }

    /**
     * Returns the FileEntry or DirectoryEntry corresponding to the selected item, or null
     * if no item is selected.
     *
     * @return {?Entry}
     */
    function getSelectedItem() {
        var selected = _projectTree.jstree("get_selected");
        if (selected)
            return selected.data("entry");
        return null;
    }

    /**
     * Create a new item in the project tree.
     *
     * @param baseDir {string} Full path of the directory where the item should go
     * @param initialName {string} Initial name for the item
     * @param skipRename {boolean} If true, don't allow the user to rename the item
     * @return {Deferred} A $.Deferred() object that will be resolved with the FileEntry
     *  of the created object, or rejected if the user cancelled or entered an illegal
     *  filename.
     */
    function createNewItem(baseDir, initialName, skipRename) {
        // TODO: Need API to get tree node for baseDir.
        // In the meantime, pass null for node so new item is placed
        // relative to the selection
        var node = null,
            selection = _projectTree.jstree("get_selected"),
            selectionEntry = null,
            position = "inside",
            escapeKeyPressed = false,
            result = new $.Deferred();

        // get the FileEntry or DirectoryEntry
        if ( selection ) {
            selectionEntry = selection.data("entry")
        }

        // move selection to parent DirectoryEntry
        if ( selectionEntry && selectionEntry.isFile ) {
            position = "after";

            // FIXME (jasonsj): get_parent returns the tree instead of the directory?
            /*
            selection = _projectTree.jstree("get_parent", selection);

            if ( typeof( selection.data ) == "function" ) {
                // get Entry from tree node
                // note that the jstree root will return undefined
                selectionEntry = selection.data("entry");
            }
            else {
                // reset here. will be replaced with project root.
                selectionEntry = null;
            }
            */
            // FIXME (jasonsj): hackish way to get parent directory; replace with Entry.getParent() when available
            var filePath = selectionEntry.fullPath;
            selectionEntry = new NativeFileSystem.DirectoryEntry(filePath.substring(0, filePath.lastIndexOf("/")));
        }

        // use the project root DirectoryEntry
        if ( !selectionEntry ) {
            selectionEntry = getProjectRoot();
        }

        _projectTree.on("create.jstree", function(event, data) {
            $(event.target).off("create.jstree");

            function errorCleanup() {
                // TODO: If an error occurred, we should allow the user to fix the filename.
                // For now we just remove the node so you have to start again.
                _projectTree.jstree("remove", data.rslt.obj);
                result.reject();
            }

            if (!escapeKeyPressed) {
                // Validate file name
                // TODO: There are some filenames like COM1, LPT3, etc. that are not valid on Windows.
                // We may want to add checks for those here.
                // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
                if (data.rslt.name.search(/[/?*:;{}<>\\|]+/) !== -1) {
                    brackets.showModalDialog(
                            brackets.DIALOG_ID_ERROR
                        ,   Strings.INVALID_FILENAME_TITLE
                        ,   Strings.INVALID_FILENAME_MESSAGE);

                    errorCleanup();
                    return;
                }

                // Use getFile() to create the new file
                selectionEntry.getFile(data.rslt.name
                    , {create: true, exclusive: true}
                    , function( entry ) {
                        data.rslt.obj.data("entry", entry);
                        _projectTree.jstree("select_node", data.rslt.obj, true);
                        result.resolve(entry);
                    }
                    , function ( error ) {
                        if ( ( error.code === FileError.PATH_EXISTS_ERR )
                             || (error.code === FileError.TYPE_MISMATCH_ERR ) ) {
                            brackets.showModalDialog(
                                  brackets.DIALOG_ID_ERROR
                                , Strings.INVALID_FILENAME_TITLE
                                , Strings.format(
                                      Strings.FILE_ALREADY_EXISTS
                                    , data.rslt.name
                                )
                            );
                        }
                        else {
                            var errString = error.code == FileError.NO_MODIFICATION_ALLOWED_ERR ?
                                             Strings.NO_MODIFICATION_ALLOWED_ERR :
                                             Strings.format(String.GENERIC_ERROR, error.code)
                            var errMsg = Strings.format(Strings.ERROR_CREATING_FILE, data.rslt.name, errString);

                            brackets.showModalDialog(
                                  brackets.DIALOG_ID_ERROR
                                , Strings.ERROR_CREATING_FILE_TITLE
                                , errMsg
                            );
                        }

                        errorCleanup();
                    }
                );
            }
            else { //escapeKeyPressed
                errorCleanup();
            }
        });

        // Create the node and open the editor
        _projectTree.jstree("create", node, position, {data: initialName}, null, skipRename);

        var renameInput = _projectTree.find(".jstree-rename-input");

        renameInput.on("keydown", function(event) {
            // Listen for escape key on keydown, so we can remove the node in the create.jstree handler above
            if (event.keyCode == 27) {
                escapeKeyPressed = true;
            }
        });

        // TODO: Figure out better way to style this input. All styles are inlined by jsTree...
        renameInput.css(
            { left: "17px"
            , height: "24px"
            }
        ).parent().css(
            { height: "26px"
            }
        );

        return result;
    }

    /**
     * @private
     * Called by jsTree when the user has expanded a node that has never been expanded before. We call
     * jsTree back asynchronously with the node's immediate children data once the subfolder is done
     * being fetched.
     *
     * @param {jQueryObject} treeNode  jQ object for the DOM node being expanded
     * @param {function(Array)} jsTreeCallback  jsTree callback to provide children to
     */
    function _treeDataProvider(treeNode, jsTreeCallback) {
        var dirEntry;

        if (treeNode == -1) {
            // Special case: root of tree
            dirEntry = _projectRoot;
        } else {
            // All other nodes: the DirectoryEntry is saved as jQ data in the tree (by _convertEntriesToJSON())
            dirEntry = treeNode.data("entry");
        }

        // Fetch dirEntry's contents
        dirEntry.createReader().readEntries(
            function(entries) {
                var subtreeJSON = _convertEntriesToJSON(entries);
                //If the list is empty, add an empty object so the loading message goes away
                if( subtreeJSON.length === 0 )
                    subtreeJSON.push({});
                jsTreeCallback(subtreeJSON);
            },
            function(error) {
                brackets.showModalDialog(
                      brackets.DIALOG_ID_ERROR
                    , Strings.ERROR_LOADING_PROJECT
                    , Strings.format(Strings.READ_DIRECTORY_ENTRIES_ERROR, dirEntry.fullPath, error.code)
                );
            }
        );

    }

    /**
     * @private
     * Given an array of NativeFileSystem entries, returns a JSON array representing them in the format
     * required by jsTree. Saves the corresponding Entry object as metadata (which jsTree will store in
     * the DOM via $.data()).
     *
     * Does NOT recursively traverse the file system: folders are marked as expandable but are given no
     * children initially.
     *
     * @param {Array.<Entry>} entries  Array of NativeFileSystem entry objects.
     * @return {Array} jsTree node data: array of JSON objects
     */
    function _convertEntriesToJSON(entries) {
        var jsonEntryList = []
        ,   entry;

        for (var entryI in entries) {
            entry = entries[entryI];

            var jsonEntry = {
                data: entry.name,
                attr: { id: "node" + _projectInitialLoad.id++ },
                metadata: { entry: entry }
            };
            if (entry.isDirectory) {
                jsonEntry.children = [];
                jsonEntry.state = "closed";
            }

            // For more info on jsTree's JSON format see: http://www.jstree.com/documentation/json_data
            jsonEntryList.push(jsonEntry);

            // Map path to ID to initialize loaded and opened states
            _projectInitialLoad.fullPathToIdMap[entry.fullPath] = jsonEntry.attr.id;
        }
        return jsonEntryList;
    }

    /**
     * @private
     * Given an input to jsTree's json_data.data setting, display the data in the file tree UI
     * (replacing any existing file tree that was previously displayed). This input could be
     * raw JSON data, or it could be a dataprovider function. See jsTree docs for details:
     * http://www.jstree.com/documentation/json_data
     */
    function _renderTree(treeDataProvider) {

        var projectTreeContainer = $("#project-files-container");

        // Instantiate tree widget
        // (jsTree is smart enough to replace the old tree if there's already one there)
        _projectTree = projectTreeContainer.jstree({
            plugins : ["ui", "themes", "json_data", "crrm"],
            json_data : { data:treeDataProvider, correct_state: false },
            core : { animation:0 },
            themes : { theme:"brackets", url:"styles/jsTreeTheme.css", dots:false, icons:false },
                //(note: our actual jsTree theme CSS lives in brackets.less; we specify an empty .css
                // file because jsTree insists on loading one itself)

            strings : { loading : "Loading ...", new_node : "New node" }    // TODO: localization
        })
        .bind("select_node.jstree", function(event, data) {
            var entry = data.rslt.obj.data("entry");
            if (entry.isFile)
                CommandManager.execute(Commands.FILE_OPEN, entry.fullPath);
        })
        .bind("reopen.jstree", function(event, data) {
            // This handler fires for the initial load and subsequent
            // reload_nodes events. For each depth level of the tree, we open
            // the saved nodes by a fullPath lookup.
            if ( _projectInitialLoad.previous.length > 0 ) {
                // load previously open nodes by increasing depth
                var toOpenPaths = _projectInitialLoad.previous.shift()
                ,   toOpenIds   = [];

                // use path to lookup ID
                $.each( toOpenPaths, function(index, value) {
                    toOpenIds.push(_projectInitialLoad.fullPathToIdMap[value]);
                });

                // specify nodes to open and load
                data.inst.data.core.to_open = toOpenIds;
                _projectTree.jstree("reload_nodes", false);
            }
        })
        .bind("dblclick.jstree", function(event) {
            var entry = $(event.target).closest("li").data("entry");
            if (entry.isFile)
                CommandManager.execute(Commands.FILE_ADD_TO_WORKING_SET, entry.fullPath);
        });
    };

    // Define public API
    exports.getProjectRoot  = getProjectRoot;
    exports.isWithinProject = isWithinProject;
    exports.makeProjectRelativeIfPossible = makeProjectRelativeIfPossible;
    exports.openProject     = openProject;
    exports.loadProject     = loadProject;
    exports.getSelectedItem = getSelectedItem;
    exports.createNewItem   = createNewItem;

    // Register save callback
    var loadedPath = window.location.pathname;
    var bracketsSrc = loadedPath.substr(0, loadedPath.lastIndexOf("/"));
    var defaults =
        { projectPath:      bracketsSrc /* initialze to brackets source */
        , projectTreeState: ""          /* TODO (jasonsj): jstree state */
        };
    PreferencesManager.addPreferencesClient(PREFERENCES_CLIENT_ID, savePreferences, this, defaults);
});


module Jasmine
  module Core
    VERSION = "1.1.0"
  end
end


#!/bin/bash
BASEDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

echo $BASEDIR

# open the Jasmine SpecRunner
open ${BASEDIR}/../../bin/mac/Brackets.app --args file:///${BASEDIR}/SpecRunner.html


var closeAllResult = CommandManager.execute(Commands.FILE_CLOSE_ALL);

keep keep keep keep replace replace replace keep keep keep keep


/* Forms.less
 * Base styles for various input types, form layouts, and states
 * ------------------------------------------------------------- */

// FORM STYLES
// -----------

form {
  margin-bottom: @baseline;
}

// Groups of fields with labels on top (legends)
fieldset {
  margin-bottom: @baseline;
  padding-top: @baseline;
  legend {
    display: block;
    padding-left: 150px;
    font-size: @basefont * 1.5;
    line-height: 1;
    color: @grayDark;
    *padding: 0 0 5px 145px; /* IE6-7 */
    *line-height: 1.5; /* IE6-7 */
  }
}

// Parent element that clears floats and wraps labels and fields together
form .clearfix {
  margin-bottom: @baseline;
  .clearfix()
}

// Set font for forms
label,
input,
select,
textarea {
  #font > .sans-serif(normal,13px,normal);
}

// Float labels left
label {
  padding-top: 6px;
  font-size: @basefont;
  line-height: @baseline;
  float: left;
  width: 130px;
  text-align: right;
  color: @grayDark;
}

// Shift over the inside div to align all label's relevant content
form .input {
  margin-left: 150px;
}

// Checkboxs and radio buttons
input[type=checkbox],
input[type=radio] {
  cursor: pointer;
}

// Inputs, Textareas, Selects
input,
textarea,
select,
.uneditable-input {
  display: inline-block;
  width: 210px;
  height: @baseline;
  padding: 4px;
  font-size: @basefont;
  line-height: @baseline;
  color: @gray;
  border: 1px solid #ccc;
  .border-radius(3px);
}

// remove padding from select
select {
  padding: initial;
}

// mini reset for non-html5 file types
input[type=checkbox],
input[type=radio] {
  width: auto;
  height: auto;
  padding: 0;
  margin: 3px 0;
  *margin-top: 0; /* IE6-7 */
  line-height: normal;
  border: none;
}

input[type=file] {
  background-color: @white;
  padding: initial;
  border: initial;
  line-height: initial;
  .box-shadow(none);
}

input[type=button],
input[type=reset],
input[type=submit] {
  width: auto;
  height: auto;
}

select,
input[type=file] {
  height: @baseline * 1.5; // In IE7, the height of the select element cannot be changed by height, only font-size
  *height: auto; // Reset for IE7
  line-height: @baseline * 1.5;
  *margin-top: 4px; /* For IE7, add top margin to align select with labels */
}

// Make multiple select elements height not fixed
select[multiple] {
  height: inherit;
  background-color: @white; // Fixes Chromium bug of unreadable items
}

textarea {
  height: auto;
}

// For text that needs to appear as an input but should not be an input
.uneditable-input {
  background-color: @white;
  display: block;
  border-color: #eee;
  .box-shadow(inset 0 1px 2px rgba(0,0,0,.025));
  cursor: not-allowed;
}

// Placeholder text gets special styles; can't be bundled together though for some reason
:-moz-placeholder {
  color: @grayLight;
}
::-webkit-input-placeholder {
  color: @grayLight;
}

// Focus states
input,
textarea {
  @transition: border linear .2s, box-shadow linear .2s;
  .transition(@transition);
  .box-shadow(inset 0 1px 3px rgba(0,0,0,.1));
}
input:focus,
textarea:focus {
  outline: 0;
  border-color: rgba(82,168,236,.8);
  @shadow: inset 0 1px 3px rgba(0,0,0,.1), 0 0 8px rgba(82,168,236,.6);
  .box-shadow(@shadow);
}
input[type=file]:focus,
input[type=checkbox]:focus,
select:focus {
  .box-shadow(none); // override for file inputs
  outline: 1px dotted #666; // Selet elements don't get box-shadow styles, so instead we do outline
}

// FORM FIELD FEEDBACK STATES
// --------------------------

// Mixin for form field states
.formFieldState(@textColor: #555, @borderColor: #ccc, @backgroundColor: #f5f5f5) {
  // Set the text color
  > label,
  .help-block,
  .help-inline {
    color: @textColor;
  }
  // Style inputs accordingly
  input,
  textarea {
    color: @textColor;
    border-color: @borderColor;
    &:focus {
      border-color: darken(@borderColor, 10%);
      .box-shadow(0 0 6px lighten(@borderColor, 20%);
    }
  }
  // Give a small background color for input-prepend/-append
  .input-prepend .add-on,
  .input-append .add-on {
    color: @textColor;
    background-color: @backgroundColor;
    border-color: @textColor;
  }
}
// Error
form .clearfix.error {
  .formFieldState(#b94a48, #ee5f5b, lighten(#ee5f5b, 30%));
}
// Warning
form .clearfix.warning {
  .formFieldState(#c09853, #ccae64, lighten(#CCAE64, 5%));
}
// Success
form .clearfix.success {
  .formFieldState(#468847, #57a957, lighten(#57a957, 30%));
}

// Form element sizes
// TODO v2: remove duplication here and just stick to .input-[size] in light of adding .spanN sizes
.input-mini,
input.mini,
textarea.mini,
select.mini {
  width: 60px;
}
.input-small,
input.small,
textarea.small,
select.small {
  width: 90px;
}
.input-medium,
input.medium,
textarea.medium,
select.medium {
  width: 150px;
}
.input-large,
input.large,
textarea.large,
select.large {
  width: 210px;
}
.input-xlarge,
input.xlarge,
textarea.xlarge,
select.xlarge {
  width: 270px;
}
.input-xxlarge,
input.xxlarge,
textarea.xxlarge,
select.xxlarge {
  width: 530px;
}
textarea.xxlarge {
  overflow-y: auto;
}

// Grid style input sizes
// This is a duplication of the main grid .columns() mixin, but subtracts 10px to account for input padding and border
.formColumns(@columnSpan: 1) {
  display: inline-block;
  float: none;
  width: ((@gridColumnWidth) * @columnSpan) + (@gridGutterWidth * (@columnSpan - 1)) - 10;
  margin-left: 0;
}
input,
textarea {
  // Default columns
  &.span1     { .formColumns(1); }
  &.span2     { .formColumns(2); }
  &.span3     { .formColumns(3); }
  &.span4     { .formColumns(4); }
  &.span5     { .formColumns(5); }
  &.span6     { .formColumns(6); }
  &.span7     { .formColumns(7); }
  &.span8     { .formColumns(8); }
  &.span9     { .formColumns(9); }
  &.span10    { .formColumns(10); }
  &.span11    { .formColumns(11); }
  &.span12    { .formColumns(12); }
  &.span13    { .formColumns(13); }
  &.span14    { .formColumns(14); }
  &.span15    { .formColumns(15); }
  &.span16    { .formColumns(16); }
}

// Disabled and read-only inputs
input[disabled],
select[disabled],
textarea[disabled],
input[readonly],
select[readonly],
textarea[readonly] {
  background-color: #f5f5f5;
  border-color: #ddd;
  cursor: not-allowed;
}

// Actions (the buttons)
.actions {
  background: #f5f5f5;
  margin-top: @baseline;
  margin-bottom: @baseline;
  padding: (@baseline - 1) 20px @baseline 150px;
  border-top: 1px solid #ddd;
  .border-radius(0 0 3px 3px);
  .secondary-action {
    float: right;
    a {
      line-height: 30px;
      &:hover {
        text-decoration: underline;
      }
    }
  }
}

// Help Text
// TODO: Do we need to set basefont and baseline here?
.help-inline,
.help-block {
  font-size: @basefont;
  line-height: @baseline;
  color: @grayLight;
}
.help-inline {
  padding-left: 5px;
  *position: relative; /* IE6-7 */
  *top: -5px; /* IE6-7 */
}

// Big blocks of help text
.help-block {
  display: block;
  max-width: 600px;
}

// Inline Fields (input fields that appear as inline objects
.inline-inputs {
  color: @gray;
  span {
    padding: 0 2px 0 1px;
  }
}

// Allow us to put symbols and text within the input field for a cleaner look
.input-prepend,
.input-append {
  input {
    .border-radius(0 3px 3px 0);
  }
  .add-on {
    position: relative;
    background: #f5f5f5;
    border: 1px solid #ccc;
    z-index: 2;
    float: left;
    display: block;
    width: auto;
    min-width: 16px;
    height: 18px;
    padding: 4px 4px 4px 5px;
    margin-right: -1px;
    font-weight: normal;
    line-height: 18px;
    color: @grayLight;
    text-align: center;
    text-shadow: 0 1px 0 @white;
    .border-radius(3px 0 0 3px);
  }
  .active {
    background: lighten(@green, 30);
    border-color: @green;
  }
}
.input-prepend {
  .add-on {
    *margin-top: 1px; /* IE6-7 */
  }
}
.input-append {
  input {
    float: left;
    .border-radius(3px 0 0 3px);
  }
  .add-on {
    .border-radius(0 3px 3px 0);
    margin-right: 0;
    margin-left: -1px;
  }
}

// Stacked options for forms (radio buttons or checkboxes)
.inputs-list {
  margin: 0 0 5px;
  width: 100%;
  li {
    display: block;
    padding: 0;
    width: 100%;
  }
  label {
    display: block;
    float: none;
    width: auto;
    padding: 0;
    margin-left: 20px;
    line-height: @baseline;
    text-align: left;
    white-space: normal;
    strong {
      color: @gray;
    }
    small {
      font-size: @basefont - 2;
      font-weight: normal;
    }
  }
  .inputs-list {
    margin-left: 25px;
    margin-bottom: 10px;
    padding-top: 0;
  }
  &:first-child {
    padding-top: 6px;
  }
  li + li {
    padding-top: 2px;
  }
  input[type=radio],
  input[type=checkbox] {
    margin-bottom: 0;
    margin-left: -20px;
    float: left;
  }
}

// Stacked forms
.form-stacked {
  padding-left: 20px;
  fieldset {
    padding-top: @baseline / 2;
  }
  legend {
    padding-left: 0;
  }
  label {
    display: block;
    float: none;
    width: auto;
    font-weight: bold;
    text-align: left;
    line-height: 20px;
    padding-top: 0;
  }
  .clearfix {
    margin-bottom: @baseline / 2;
    div.input {
      margin-left: 0;
    }
  }
  .inputs-list {
    margin-bottom: 0;
    li {
      padding-top: 0;
      label {
        font-weight: normal;
        padding-top: 0;
      }
    }
  }
  div.clearfix.error {
    padding-top: 10px;
    padding-bottom: 10px;
    padding-left: 10px;
    margin-top: 0;
    margin-left: -10px;
  }
  .actions {
    margin-left: -20px;
    padding-left: 20px;
  }
}


/*!
 * Bootstrap v1.4.0
 *
 * Copyright 2011 Twitter, Inc
 * Licensed under the Apache License v2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Designed and built with all the love in the world @twitter by @mdo and @fat.
 * Date: Thu Jan  5 07:14:51 PST 2012
 */

// CSS Reset
@import "reset.less";

// Core variables and mixins
@import "variables.less"; // Modify this for custom colors, font-sizes, etc
@import "mixins.less";

// Grid system and page structure
@import "scaffolding.less";

// Styled patterns and elements
@import "type.less";
@import "forms.less";
@import "tables.less";
@import "patterns.less";