зеркало из https://github.com/mozilla/brackets.git
Initial work on appshell shim in browser
Remove warning dialog for brackets in browser. Switch to Filer for all fs operations, update to latest API. NOTE: requires patched filer.js25f2a24cdf
Update filer to v0.0.13 to fix buffer regression Get initial project loading and File > Open working (style issues on dialog still) Change how styles are loaded First working File > Open Add sample file system generation Remove Filer submodule Add MakeDrive repo as submodule Switch things from Filer.* to MakeDrive.* Added save as functionality to UI Add makedrive sync logic in brackets Fix how trailing slashes are removed from dir names Added code to choose which extensions run in a browser environment Fix shared watches, update to auto-sync in MakeDrive vs. manual, use Path.normalize jquery module imports for sync-icon extension Update makedrive and makedrive-sync-icon submodules Allow withCredentials flag Update to use new MakeDrive client api for connect() without token Update MakeDrive for8d6f548f72
Update MakeDrive for CORS fixes Update MakeDrive with built version Update makedrive for CORS fix https://github.com/mozilla/makedrive/pull/149 Update README Update MakeDrive to v0.0.12 for release Update MakeDrive to get Filer fix for sh.cwd/pwd() Update makedrive Update MakeDrive submodule update submodule for makedrive Update MakeDrive submodule 08-08-2014 - 10:57AM EST Update MakeDrive submodule
This commit is contained in:
Родитель
eceb1d6a2f
Коммит
fa5d187ff4
|
@ -25,3 +25,9 @@
|
|||
[submodule "src/extensions/default/JSLint/thirdparty/jslint"]
|
||||
path = src/extensions/default/JSLint/thirdparty/jslint
|
||||
url = https://github.com/peterflynn/JSLint.git
|
||||
[submodule "src/thirdparty/makedrive"]
|
||||
path = src/thirdparty/makedrive
|
||||
url = https://github.com/mozilla/makedrive.git
|
||||
[submodule "src/extensions/default/makedrive-sync-icon"]
|
||||
path = src/extensions/default/makedrive-sync-icon
|
||||
url = git@github.com:cdot-brackets-extensions/makedrive-sync-icon.git
|
||||
|
|
125
README.md
125
README.md
|
@ -1,21 +1,9 @@
|
|||
Welcome to Brackets! [![Build Status](https://travis-ci.org/adobe/brackets.svg?branch=master)](https://travis-ci.org/adobe/brackets)
|
||||
-------------------
|
||||
# Bramble is based on Brackets
|
||||
|
||||
Brackets is a modern open-source code editor for HTML, CSS
|
||||
and JavaScript that's *built* in HTML, CSS and JavaScript.
|
||||
|
||||
What makes Brackets different from other web code editors?
|
||||
|
||||
* **Tools shouldn't get in your way.** Instead of cluttering up your coding
|
||||
environment with lots of panels and icons, the Quick Edit UI in Brackets puts
|
||||
context-specific code and tools inline.
|
||||
* **Brackets is in sync with your browser.** With Live Preview, Brackets
|
||||
works directly with your browser to push code edits instantly and jump
|
||||
back and forth between your real source code and the browser view.
|
||||
* **Do it yourself.** Because Brackets is open source, and built with HTML, CSS
|
||||
and JavaScript, you can [help build](https://github.com/adobe/brackets/blob/master/CONTRIBUTING.md) the best code editor for the web.
|
||||
|
||||
Brackets may have reached version 1, but we're not stopping there. We have many feature ideas on our
|
||||
Brackets is at 1.0 and we're not stopping there. We have many feature ideas on our
|
||||
[trello board](http://bit.ly/BracketsTrelloBoard) that we're anxious to add and other
|
||||
innovative web development workflows that we're planning to build into Brackets.
|
||||
So take Brackets out for a spin and let us know how we can make it your favorite editor.
|
||||
|
@ -24,96 +12,47 @@ You can see some
|
|||
[screenshots of Brackets](https://github.com/adobe/brackets/wiki/Brackets-Screenshots)
|
||||
on the wiki, [intro videos](http://www.youtube.com/user/CodeBrackets) on YouTube, and news on the [Brackets blog](http://blog.brackets.io/).
|
||||
|
||||
How to install and run Brackets
|
||||
-------------------------------
|
||||
#### Download
|
||||
|
||||
Installers for the latest stable build for Mac, Windows and Linux (Debian/Ubuntu) can be [downloaded here](http://brackets.io/).
|
||||
|
||||
The Linux version has most of the features of the Mac and Windows versions, but
|
||||
is still missing a few things. See the [Linux wiki page](https://github.com/adobe/brackets/wiki/Linux-Version)
|
||||
for a list of known issues and to find out how you can help.
|
||||
|
||||
#### Usage
|
||||
|
||||
By default, Brackets opens a folder containing some simple "Getting Started" content.
|
||||
You can choose a different folder to edit using *File > Open Folder*.
|
||||
|
||||
Most of Brackets should be pretty self-explanatory, but for information on how
|
||||
to use its unique features, like Quick Edit and Live Preview, please read
|
||||
[How to Use Brackets](http://github.com/adobe/brackets/wiki/How-to-Use-Brackets).
|
||||
Also, see the [release notes](http://github.com/adobe/brackets/wiki/Release-Notes)
|
||||
for a list of new features and known issues in each build.
|
||||
|
||||
In addition to the core features built into Brackets, there is a large and growing
|
||||
community of developers building extensions that add all sorts of useful functionality.
|
||||
See the [Brackets Extension Registry](https://brackets-registry.aboutweb.com/)
|
||||
for a list of available extensions. For installation instructions,
|
||||
see the [extensions wiki page](https://github.com/adobe/brackets/wiki/Brackets-Extensions).
|
||||
|
||||
#### Need help?
|
||||
|
||||
Having problems starting Brackets the first time, or not sure how to use Brackets? Please
|
||||
review [Troubleshooting](https://github.com/adobe/brackets/wiki/Troubleshooting), which helps
|
||||
you to fix common problems and find extra help if needed.
|
||||
|
||||
|
||||
Helping Brackets
|
||||
----------------
|
||||
|
||||
#### I found a bug!
|
||||
|
||||
If you found a repeatable bug, and [troubleshooting](https://github.com/adobe/brackets/wiki/Troubleshooting)
|
||||
tips didn't help, then be sure to [search existing issues](https://github.com/adobe/brackets/issues) first.
|
||||
Include steps to consistently reproduce the problem, actual vs. expected results, screenshots, and your OS and
|
||||
Brackets version number. Disable all extensions to verify the issue is a core Brackets bug.
|
||||
[Read more guidelines for filing good bugs.](https://github.com/adobe/brackets/wiki/How-to-Report-an-Issue)
|
||||
|
||||
|
||||
#### I have a new suggestion, but don't know how to program!
|
||||
|
||||
For feature requests please first check our [Trello board](http://bit.ly/BracketsBacklog) to
|
||||
see if it's already there; you can upvote it if so. If not, feel free to file it as an issue as above; we'll
|
||||
move it to the feature backlog for you.
|
||||
|
||||
|
||||
#### I want to help with the code!
|
||||
|
||||
Awesome! _There are lots of ways you can help._ First read
|
||||
[CONTRIBUTING.md](https://github.com/adobe/brackets/blob/master/CONTRIBUTING.md),
|
||||
then learn how to [pull the repo and hack on Brackets](https://github.com/adobe/brackets/wiki/How-to-Hack-on-Brackets).
|
||||
|
||||
The text editor inside Brackets is based on
|
||||
[CodeMirror](http://github.com/codemirror/CodeMirror)—thanks to Marijn for
|
||||
taking our pull requests, implementing feature requests and fixing bugs! See
|
||||
[Notes on CodeMirror](https://github.com/adobe/brackets/wiki/Notes-on-CodeMirror)
|
||||
for info on how we're using CodeMirror.
|
||||
|
||||
Although Brackets is built in HTML/CSS/JS, it currently runs as a desktop
|
||||
application in a thin native shell, so that it can access your local files.
|
||||
(If you just try to open the index.html file in a browser, it won't work yet.)
|
||||
The native shell for Brackets lives in a separate repo,
|
||||
[adobe/brackets-shell](https://github.com/adobe/brackets-shell/).
|
||||
# How to setup Bramble (Brackets) in your local machine
|
||||
|
||||
Step 01: Make sure you fork and clone [Bramble](https://github.com/humphd/brackets).
|
||||
|
||||
I want to keep track of how Brackets is doing!
|
||||
----------------------------------------------
|
||||
```
|
||||
$ git clone https://github.com/[yourusername]/brackets --recursive
|
||||
```
|
||||
|
||||
Not sure you needed the exclamation point there, but we like your enthusiasm.
|
||||
Step 02: Install its dependencies
|
||||
|
||||
#### What's Brackets working on next?
|
||||
Navigate to the root of the directory you cloned and run
|
||||
|
||||
* In our [feature backlog](http://bit.ly/BracketsBacklog), the columns to the right
|
||||
(starting from "Development") list the features that we're currently working on.
|
||||
"Ready" shows what we'll be working on next.
|
||||
* Watch our [GitHub activity stream](https://github.com/adobe/brackets/pulse).
|
||||
* Watch our [Waffle Kanban board](https://waffle.io/adobe/brackets): Work items in [![Stories in Ready](https://badge.waffle.io/adobe/brackets.svg?label=ready&title=Ready)](http://waffle.io/adobe/brackets) are next. The entire development process is outlined in the [Developer Guide](https://github.com/adobe/brackets/wiki/Brackets-Developers-Guide).
|
||||
```
|
||||
$ npm install
|
||||
```
|
||||
|
||||
#### Contact info
|
||||
```
|
||||
$ git submodule update --init
|
||||
```
|
||||
|
||||
* **Slack:** [Brackets on Slack](https://brackets.slack.com) (You can join by [requesting an invite](https://brackets-slack.herokuapp.com/))
|
||||
* **Developers mailing list:** http://groups.google.com/group/brackets-dev
|
||||
* **Twitter:** [@brackets](https://twitter.com/brackets)
|
||||
* **Blog:** http://blog.brackets.io/
|
||||
* **IRC:** [#brackets on freenode](http://webchat.freenode.net/?channels=brackets)
|
||||
```
|
||||
Grunt commands to be added
|
||||
```
|
||||
|
||||
Step 03: Run Bramble:
|
||||
|
||||
Run one of the suggested local servers (or your own) from the root directory of Bramble:
|
||||
* [Apache Webserver](http://www.apache.org/)
|
||||
* Host on [github pages](https://help.github.com/articles/what-are-github-pages)
|
||||
* [Python WebServer](https://docs.python.org/2/library/simplehttpserver.html)
|
||||
|
||||
Assuming you have Bramble running on port `8080`. Now you can visit [http://localhost:8080/src](http://localhost:8080/src).
|
||||
|
||||
--------------
|
||||
|
||||
## After installation
|
||||
|
||||
After you have everything setup, you can now run the server you chose in the root of your local Bramble directory and see it in action by visiting [http://localhost:8080/src](http://localhost:8080/src).
|
||||
|
|
|
@ -211,15 +211,6 @@ define(function (require, exports, module) {
|
|||
function _onReady() {
|
||||
PerfUtils.addMeasurement("window.document Ready");
|
||||
|
||||
// Let the user know Brackets doesn't run in a web browser yet
|
||||
if (brackets.inBrowser) {
|
||||
Dialogs.showModalDialog(
|
||||
DefaultDialogs.DIALOG_ID_ERROR,
|
||||
Strings.ERROR_IN_BROWSER_TITLE,
|
||||
Strings.ERROR_IN_BROWSER
|
||||
);
|
||||
}
|
||||
|
||||
// Use quiet scrollbars if we aren't on Lion. If we're on Lion, only
|
||||
// use native scroll bars when the mouse is not plugged in or when
|
||||
// using the "Always" scroll bar setting.
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 82bab57ae85d5cf26fc76bfb89a5de53cdb7b778
|
|
@ -0,0 +1,354 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
|
||||
/*global define, appshell, $, window */
|
||||
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var FileSystemError = require("filesystem/FileSystemError"),
|
||||
FileSystemStats = require("filesystem/FileSystemStats"),
|
||||
Dialogs = require("widgets/Dialogs"),
|
||||
DefaultDialogs = require("widgets/DefaultDialogs"),
|
||||
// TODO: we have to figure out how we're going to build/deploy makedrive.js, this is hacky.
|
||||
// since it requires a manual `grunt build` step in src/thirdparty/makedrive
|
||||
MakeDrive = require("thirdparty/makedrive/client/dist/makedrive"),
|
||||
OpenDialog = require("filesystem/impls/makedrive/open-dialog");
|
||||
|
||||
var fs = MakeDrive.fs(),
|
||||
Path = MakeDrive.Path,
|
||||
watchers = {};
|
||||
|
||||
var _changeCallback; // Callback to notify FileSystem of watcher changes
|
||||
|
||||
// Give extensions access to MakeDrive's sync functionality. The hosting app
|
||||
// needs to call sync.connect(serverURL) when the user is logged in, for example.
|
||||
appshell.MakeDrive = MakeDrive;
|
||||
|
||||
var sync = fs.sync;
|
||||
|
||||
// Try to upgrade to a syncing filesystem
|
||||
sync.connect('ws://localhost:9090');
|
||||
|
||||
//TODO: Do we want to do anything other than console.log for all these events?
|
||||
sync.on('syncing', function() {
|
||||
console.log('sync started');
|
||||
});
|
||||
sync.on('error', function(e) {
|
||||
console.log('sync error: ', e);
|
||||
});
|
||||
sync.on('completed', function() {
|
||||
console.log('sync completed');
|
||||
});
|
||||
sync.on('updates', function() {
|
||||
console.log('server has updates');
|
||||
});
|
||||
|
||||
function showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, callback) {
|
||||
OpenDialog.showOpenDialog.apply(null, arguments);
|
||||
}
|
||||
|
||||
function showSaveDialog(title, initialPath, defaultName, callback) {
|
||||
var selectedPath;
|
||||
var saveResponse = window.prompt(title, defaultName);
|
||||
if(saveResponse){
|
||||
initialPath = initialPath || '/';
|
||||
selectedPath = initialPath + saveResponse;
|
||||
callback(null, selectedPath);
|
||||
}
|
||||
else{
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Filer error codes to FileSystemError values.
|
||||
*
|
||||
* @param {?number} err A Filer error code
|
||||
* @return {?string} A FileSystemError string, or null if there was no error code.
|
||||
* @private
|
||||
**/
|
||||
function _mapError(err) {
|
||||
if (!err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (err.code) {
|
||||
case 'EINVAL':
|
||||
return FileSystemError.INVALID_PARAMS;
|
||||
case 'ENOENT':
|
||||
return FileSystemError.NOT_FOUND;
|
||||
case 'EROFS':
|
||||
return FileSystemError.NOT_WRITABLE;
|
||||
case 'ENOSPC':
|
||||
return FileSystemError.OUT_OF_SPACE;
|
||||
case 'ENOTEMPTY':
|
||||
case 'EEXIST':
|
||||
return FileSystemError.ALREADY_EXISTS;
|
||||
case 'ENOTDIR':
|
||||
return FileSystemError.INVALID_PARAMS;
|
||||
case 'EBADF':
|
||||
return FileSystemError.NOT_READABLE;
|
||||
}
|
||||
|
||||
return FileSystemError.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a callback to one that transforms its first parameter from a
|
||||
* Filer error code to a FileSystemError string.
|
||||
*
|
||||
* @param {function(?number)} cb A callback that expects an Filer error code
|
||||
* @return {function(?string)} A callback that expects a FileSystemError string
|
||||
* @private
|
||||
**/
|
||||
function _wrap(cb) {
|
||||
return function (err) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args[0] = _mapError(args[0]);
|
||||
cb.apply(null, args);
|
||||
};
|
||||
}
|
||||
|
||||
function stat(path, callback) {
|
||||
fs.stat(path, function(err, stats) {
|
||||
if (err){
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
|
||||
var mtime = new Date(stats.mtime);
|
||||
|
||||
var options = {
|
||||
isFile: stats.isFile(),
|
||||
mtime: mtime,
|
||||
size: stats.size,
|
||||
// TODO: figure out how to deal with realPath
|
||||
realPath: path,
|
||||
hash: mtime.getTime()
|
||||
};
|
||||
|
||||
var fsStats = new FileSystemStats(options);
|
||||
|
||||
callback(null, fsStats);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function exists(path, callback) {
|
||||
fs.exists(path, function(exists) {
|
||||
callback(null, exists);
|
||||
});
|
||||
}
|
||||
|
||||
function readdir(path, callback) {
|
||||
path = Path.normalize(path);
|
||||
|
||||
fs.readdir(path, function (err, contents) {
|
||||
if (err) {
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
|
||||
var count = contents.length;
|
||||
if (!count) {
|
||||
callback(null, [], []);
|
||||
return;
|
||||
}
|
||||
|
||||
var stats = [];
|
||||
contents.forEach(function (val, idx) {
|
||||
stat(Path.join(path, val), function (err, stat) {
|
||||
stats[idx] = err || stat;
|
||||
count--;
|
||||
if (count <= 0) {
|
||||
callback(null, contents, stats);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mkdir(path, mode, callback) {
|
||||
if(typeof mode === 'function') {
|
||||
callback = mode;
|
||||
}
|
||||
|
||||
fs.mkdir(path, mode, function (err) {
|
||||
if (err) {
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
stat(path, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function rename(oldPath, newPath, callback) {
|
||||
fs.rename(oldPath, newPath, _wrap(callback));
|
||||
}
|
||||
|
||||
function readFile(path, options, callback) {
|
||||
if(typeof options === 'function') {
|
||||
callback = options;
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
var encoding = options.encoding || "utf8";
|
||||
|
||||
// Execute the read and stat calls in parallel. Callback early if the
|
||||
// read call completes first with an error; otherwise wait for both
|
||||
// to finish.
|
||||
var done = false, data, stat, err;
|
||||
|
||||
if (options.stat) {
|
||||
done = true;
|
||||
stat = options.stat;
|
||||
} else {
|
||||
exports.stat(path, function (_err, _stat) {
|
||||
if (done) {
|
||||
callback(_err, _err ? null : data, _stat);
|
||||
} else {
|
||||
done = true;
|
||||
stat = _stat;
|
||||
err = _err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fs.readFile(path, encoding, function (_err, _data) {
|
||||
if (_err) {
|
||||
callback(_mapError(_err));
|
||||
return;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
callback(err, err ? null : _data, stat);
|
||||
} else {
|
||||
done = true;
|
||||
data = _data;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeFile(path, data, options, callback) {
|
||||
if(typeof options === 'function') {
|
||||
callback = options;
|
||||
}
|
||||
options = options || {};
|
||||
|
||||
var encoding = options.encoding || "utf8";
|
||||
|
||||
function _finishWrite(created) {
|
||||
fs.writeFile(path, data, encoding, function (err) {
|
||||
if (err) {
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
stat(path, function (err, stat) {
|
||||
callback(err, stat, created);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stat(path, function (err, stats) {
|
||||
if (err) {
|
||||
switch (err) {
|
||||
case FileSystemError.NOT_FOUND:
|
||||
_finishWrite(true);
|
||||
break;
|
||||
default:
|
||||
callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.hasOwnProperty("expectedHash") && options.expectedHash !== stats._hash) {
|
||||
console.error("Blind write attempted: ", path, stats._hash, options.expectedHash);
|
||||
|
||||
if (options.hasOwnProperty("expectedContents")) {
|
||||
fs.readFile(path, encoding, function (_err, _data) {
|
||||
if (_err || _data !== options.expectedContents) {
|
||||
callback(FileSystemError.CONTENTS_MODIFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
_finishWrite(false);
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
callback(FileSystemError.CONTENTS_MODIFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_finishWrite(false);
|
||||
});
|
||||
}
|
||||
|
||||
function unlink(path, callback) {
|
||||
fs.unlink(path, function(err){
|
||||
callback(_mapError(err));
|
||||
});
|
||||
}
|
||||
|
||||
function moveToTrash(path, callback) {
|
||||
// TODO: do we want to support a .trash/ dir or the like?
|
||||
unlink(path, callback);
|
||||
}
|
||||
|
||||
function initWatchers(changeCallback, offlineCallback) {
|
||||
_changeCallback = changeCallback;
|
||||
}
|
||||
|
||||
function watchPath(path, callback) {
|
||||
path = Path.normalize(path);
|
||||
|
||||
if(watchers[path]) {
|
||||
return;
|
||||
}
|
||||
watchers[path] = fs.watch(path, {recursive: true}, function(event, filename) {
|
||||
stat(filename, function(err, stats) {
|
||||
if(err) {
|
||||
return;
|
||||
}
|
||||
_changeCallback(filename, stats);
|
||||
});
|
||||
});
|
||||
callback();
|
||||
}
|
||||
|
||||
function unwatchPath(path, callback) {
|
||||
path = Path.normalize(path);
|
||||
|
||||
if(watchers[path]) {
|
||||
watchers[path].close();
|
||||
delete watchers[path];
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
function unwatchAll(callback) {
|
||||
Object.keys(watchers).forEach(function(path) {
|
||||
unwatchPath(path, function(){});
|
||||
});
|
||||
callback();
|
||||
}
|
||||
|
||||
// Export public API
|
||||
exports.showOpenDialog = showOpenDialog;
|
||||
exports.showSaveDialog = showSaveDialog;
|
||||
exports.exists = exists;
|
||||
exports.readdir = readdir;
|
||||
exports.mkdir = mkdir;
|
||||
exports.rename = rename;
|
||||
exports.stat = stat;
|
||||
exports.readFile = readFile;
|
||||
exports.writeFile = writeFile;
|
||||
exports.unlink = unlink;
|
||||
exports.moveToTrash = moveToTrash;
|
||||
exports.initWatchers = initWatchers;
|
||||
exports.watchPath = watchPath;
|
||||
exports.unwatchPath = unwatchPath;
|
||||
exports.unwatchAll = unwatchAll;
|
||||
|
||||
exports.recursiveWatch = true;
|
||||
exports.normalizeUNCPaths = false;
|
||||
});
|
|
@ -0,0 +1,316 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
|
||||
/*global define, appshell, $, window */
|
||||
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var FileSystemError = require("filesystem/FileSystemError"),
|
||||
FileSystemStats = require("filesystem/FileSystemStats"),
|
||||
Dialogs = require("widgets/Dialogs"),
|
||||
DefaultDialogs = require("widgets/DefaultDialogs"),
|
||||
Filer = require("thirdparty/filer/dist/filer.js");
|
||||
|
||||
var fs = new Filer.FileSystem({ provider: new Filer.FileSystem.providers.Fallback() }),
|
||||
Path = Filer.Path,
|
||||
watchers = {};
|
||||
|
||||
var _changeCallback; // Callback to notify FileSystem of watcher changes
|
||||
|
||||
function showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, callback) {
|
||||
// FIXME: Here we need to create a new dialog that at least
|
||||
// lists all files/folders on filesystem
|
||||
// require("text!htmlContent/filesystem-dialog.html");
|
||||
// 17:51 pflynn: oh, you can store .html templates in your extension
|
||||
// itself... you can load them with require() and then just pass
|
||||
// them to Dialogs.showModalDialogUsingTemplate() driectly
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
function showSaveDialog(title, initialPath, x, callback) {
|
||||
// FIXME
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip trailing slash (or multiple slashes) from a path
|
||||
*/
|
||||
function stripTrailingSlash(path) {
|
||||
return path.replace(/\/*$/, '');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Filer error codes to FileSystemError values.
|
||||
*
|
||||
* @param {?number} err A Filer error code
|
||||
* @return {?string} A FileSystemError string, or null if there was no error code.
|
||||
* @private
|
||||
**/
|
||||
function _mapError(err) {
|
||||
if (!err) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (err.code) {
|
||||
case 'EINVAL':
|
||||
return FileSystemError.INVALID_PARAMS;
|
||||
case 'ENOENT':
|
||||
return FileSystemError.NOT_FOUND;
|
||||
case 'EROFS':
|
||||
return FileSystemError.NOT_WRITABLE;
|
||||
case 'ENOSPC':
|
||||
return FileSystemError.OUT_OF_SPACE;
|
||||
case 'ENOTEMPTY':
|
||||
case 'EEXIST':
|
||||
return FileSystemError.ALREADY_EXISTS;
|
||||
case 'ENOTDIR':
|
||||
return FileSystemError.INVALID_PARAMS;
|
||||
case 'EBADF':
|
||||
return FileSystemError.NOT_READABLE;
|
||||
}
|
||||
|
||||
return FileSystemError.UNKNOWN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a callback to one that transforms its first parameter from a
|
||||
* Filer error code to a FileSystemError string.
|
||||
*
|
||||
* @param {function(?number)} cb A callback that expects an Filer error code
|
||||
* @return {function(?string)} A callback that expects a FileSystemError string
|
||||
* @private
|
||||
**/
|
||||
function _wrap(cb) {
|
||||
return function (err) {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args[0] = _mapError(args[0]);
|
||||
cb.apply(null, args);
|
||||
};
|
||||
}
|
||||
|
||||
function stat(path, callback) {
|
||||
fs.stat(path, function(err, stats) {
|
||||
if (err){
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
|
||||
var mtime = new Date(stats.mtime);
|
||||
|
||||
var options = {
|
||||
isFile: stats.isFile(),
|
||||
mtime: mtime,
|
||||
size: stats.size,
|
||||
// TODO: figure out how to deal with realPath
|
||||
realPath: path,
|
||||
hash: mtime.getTime()
|
||||
};
|
||||
|
||||
var fsStats = new FileSystemStats(options);
|
||||
|
||||
callback(null, fsStats);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function exists(path, callback) {
|
||||
fs.exists(path, function(exists) {
|
||||
callback(null, exists);
|
||||
});
|
||||
}
|
||||
|
||||
function readdir(path, callback) {
|
||||
path = stripTrailingSlash(path);
|
||||
fs.readdir(path, function (err, contents) {
|
||||
if (err) {
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
|
||||
var count = contents.length;
|
||||
if (!count) {
|
||||
callback(null, [], []);
|
||||
return;
|
||||
}
|
||||
|
||||
var stats = [];
|
||||
contents.forEach(function (val, idx) {
|
||||
stat(Path.join(path, val), function (err, stat) {
|
||||
stats[idx] = err || stat;
|
||||
count--;
|
||||
if (count <= 0) {
|
||||
callback(null, contents, stats);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function mkdir(path, mode, callback) {
|
||||
fs.mkdir(path, mode, function (err) {
|
||||
if (err) {
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
stat(path, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function rename(oldPath, newPath, callback) {
|
||||
fs.rename(oldPath, newPath, _wrap(callback));
|
||||
}
|
||||
|
||||
function readFile(path, options, callback) {
|
||||
var encoding = options.encoding || "utf8";
|
||||
|
||||
// Execute the read and stat calls in parallel. Callback early if the
|
||||
// read call completes first with an error; otherwise wait for both
|
||||
// to finish.
|
||||
var done = false, data, stat, err;
|
||||
|
||||
if (options.stat) {
|
||||
done = true;
|
||||
stat = options.stat;
|
||||
} else {
|
||||
exports.stat(path, function (_err, _stat) {
|
||||
if (done) {
|
||||
callback(_err, _err ? null : data, _stat);
|
||||
} else {
|
||||
done = true;
|
||||
stat = _stat;
|
||||
err = _err;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fs.readFile(path, encoding, function (_err, _data) {
|
||||
if (_err) {
|
||||
callback(_mapError(_err));
|
||||
return;
|
||||
}
|
||||
|
||||
if (done) {
|
||||
callback(err, err ? null : _data, stat);
|
||||
} else {
|
||||
done = true;
|
||||
data = _data;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function writeFile(path, data, options, callback) {
|
||||
var encoding = options.encoding || "utf8";
|
||||
|
||||
function _finishWrite(created) {
|
||||
fs.writeFile(path, data, encoding, function (err) {
|
||||
if (err) {
|
||||
callback(_mapError(err));
|
||||
return;
|
||||
}
|
||||
stat(path, function (err, stat) {
|
||||
callback(err, stat, created);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stat(path, function (err, stats) {
|
||||
if (err) {
|
||||
switch (err) {
|
||||
case FileSystemError.NOT_FOUND:
|
||||
_finishWrite(true);
|
||||
break;
|
||||
default:
|
||||
callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.hasOwnProperty("expectedHash") && options.expectedHash !== stats._hash) {
|
||||
console.error("Blind write attempted: ", path, stats._hash, options.expectedHash);
|
||||
|
||||
if (options.hasOwnProperty("expectedContents")) {
|
||||
fs.readFile(path, encoding, function (_err, _data) {
|
||||
if (_err || _data !== options.expectedContents) {
|
||||
callback(FileSystemError.CONTENTS_MODIFIED);
|
||||
return;
|
||||
}
|
||||
|
||||
_finishWrite(false);
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
callback(FileSystemError.CONTENTS_MODIFIED);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_finishWrite(false);
|
||||
});
|
||||
}
|
||||
|
||||
function unlink(path, callback) {
|
||||
fs.unlink(path, function(err){
|
||||
callback(_mapError(err));
|
||||
});
|
||||
}
|
||||
|
||||
function moveToTrash(path, callback) {
|
||||
// TODO: do we want to support a .trash/ dir or the like?
|
||||
unlink(path, callback);
|
||||
}
|
||||
|
||||
function initWatchers(changeCallback, offlineCallback) {
|
||||
_changeCallback = changeCallback;
|
||||
}
|
||||
|
||||
function watchPath(path, callback) {
|
||||
// Strip trailing slash on pathname (probably /brackets/)
|
||||
path = path.replace(/\/$/, '');
|
||||
if(watchers[path]) {
|
||||
return;
|
||||
}
|
||||
watchers[path] = fs.watch(path, {recursive: true}, function(event, filename) {
|
||||
stat(filename, function(err, stats) {
|
||||
if(err) {
|
||||
return;
|
||||
}
|
||||
_changeCallback(filename, stats);
|
||||
});
|
||||
});
|
||||
callback();
|
||||
}
|
||||
|
||||
function unwatchPath(path, callback) {
|
||||
if(watchers[path]) {
|
||||
watchers[path].close();
|
||||
delete watchers[path];
|
||||
}
|
||||
callback();
|
||||
}
|
||||
|
||||
function unwatchAll(callback) {
|
||||
Object.keys(watchers).forEach(function(path) {
|
||||
unwatchPath(path, function(){});
|
||||
});
|
||||
callback();
|
||||
}
|
||||
|
||||
// Export public API
|
||||
exports.showOpenDialog = showOpenDialog;
|
||||
exports.showSaveDialog = showSaveDialog;
|
||||
exports.exists = exists;
|
||||
exports.readdir = readdir;
|
||||
exports.mkdir = mkdir;
|
||||
exports.rename = rename;
|
||||
exports.stat = stat;
|
||||
exports.readFile = readFile;
|
||||
exports.writeFile = writeFile;
|
||||
exports.unlink = unlink;
|
||||
exports.moveToTrash = moveToTrash;
|
||||
exports.initWatchers = initWatchers;
|
||||
exports.watchPath = watchPath;
|
||||
exports.unwatchPath = unwatchPath;
|
||||
exports.unwatchAll = unwatchAll;
|
||||
|
||||
exports.recursiveWatch = true;
|
||||
exports.normalizeUNCPaths = false;
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
|
||||
/*global define, appshell, $, window */
|
||||
|
||||
// Generate a sample set of files and folders
|
||||
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
exports.ensureSampleFileSystem = function(fs) {
|
||||
|
||||
var create = function createFileIfMissing(path, contents, callback) {
|
||||
callback = callback || function(){};
|
||||
fs.exists(path, function(exists) {
|
||||
if(exists) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
fs.writeFile(path, contents, callback);
|
||||
});
|
||||
};
|
||||
|
||||
create('/index.html', '<html>\n<head><link rel="stylesheet" type="text/css" href="styles.css"></head>\n<body>\n<p>Why hello there, Brackets running in a browser!</p>\n</body>\n</html>');
|
||||
create('/styles.css', 'p { color: green; }');
|
||||
|
||||
/**
|
||||
fs.mkdir('/project1', function() {
|
||||
create('/project1/index.html', '<script src="code.js"></script>');
|
||||
// create('/project1/code.js', require('text!filesystem/impls/makedrive/MakeDriveFileSystem.js'));
|
||||
});
|
||||
|
||||
fs.mkdir('/dialogs', function() {
|
||||
create('/dialogs/open-dialog.html', require('text!filesystem/impls/makedrive/open-dialog.html'));
|
||||
create('/dialogs/open-dialog.js', require('text!filesystem/impls/makedrive/open-dialog.js'));
|
||||
});
|
||||
|
||||
**/
|
||||
};
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
<div class="makedrive modal">
|
||||
<div class="modal-header">
|
||||
<h1 class="dialog-title">{{title}}</h1>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<div class="open-files-container jstree-makedrive"></div>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="dialog-button btn" data-button-id="cancel">{{cancel}}</button>
|
||||
<button class="dialog-button btn" data-button-id="open">{{open}}</button>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,132 @@
|
|||
/*global define, $, brackets, Mustache, console */
|
||||
define(function (require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var _ = require("thirdparty/lodash");
|
||||
var Dialogs = require("widgets/Dialogs");
|
||||
var FileSystem = require("filesystem/FileSystem");
|
||||
var ViewUtils = brackets.getModule("utils/ViewUtils");
|
||||
var openDialog = require("text!filesystem/impls/makedrive/open-dialog.html");
|
||||
|
||||
var nodeId = 0;
|
||||
|
||||
function fileToTreeJSON(file) {
|
||||
var json = {
|
||||
data: file.name,
|
||||
attr: { id: "node" + nodeId++ },
|
||||
metadata: {
|
||||
file: file
|
||||
}
|
||||
};
|
||||
|
||||
if (file.isDirectory) {
|
||||
json.children = [];
|
||||
json.state = "closed";
|
||||
json.data = _.escape(json.data);
|
||||
} else {
|
||||
json.data = ViewUtils.getFileEntryDisplay(file);
|
||||
}
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
// TODO: support all args here
|
||||
function showOpenDialog(allowMultipleSelection, chooseDirectories, title, initialPath, fileTypes, callback) {
|
||||
|
||||
function initializeEventHandlers($dialog) {
|
||||
$dialog.find(".dialog-button[data-button-id='cancel']")
|
||||
.on("click", closeModal);
|
||||
|
||||
$(window).on('keydown.makedrive', function (event) {
|
||||
if (event.keyCode === 27) {
|
||||
closeModal();
|
||||
}
|
||||
});
|
||||
|
||||
$dialog.find(".dialog-button[data-button-id='open']").on("click", function () {
|
||||
var paths = $dialog.find('.jstree-clicked')
|
||||
.closest('li')
|
||||
.map(function() {
|
||||
return $(this).data().file.fullPath;
|
||||
})
|
||||
.get();
|
||||
|
||||
if (!paths.length) { return; }
|
||||
|
||||
closeModal();
|
||||
callback(null, paths);
|
||||
});
|
||||
}
|
||||
|
||||
function fileTreeDataProvider($tree, callback) {
|
||||
var directory;
|
||||
|
||||
// $tree is -1 when requesting the root
|
||||
if ($tree === -1) {
|
||||
directory = FileSystem.getDirectoryForPath(initialPath);
|
||||
} else {
|
||||
directory = $tree.data('file');
|
||||
}
|
||||
|
||||
directory.getContents(function(err, files) {
|
||||
var json = files.map(fileToTreeJSON);
|
||||
callback(json);
|
||||
});
|
||||
}
|
||||
|
||||
function handleFileDoubleClick(event) {
|
||||
var file = $(event.target).closest('li').data('file');
|
||||
|
||||
if (file && file.isFile) {
|
||||
callback(null, [file.fullPath]);
|
||||
closeModal();
|
||||
}
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
if(dialog) {
|
||||
dialog.close();
|
||||
}
|
||||
}
|
||||
|
||||
var data = {
|
||||
title: title,
|
||||
cancel: "Cancel",
|
||||
open: "Open"
|
||||
};
|
||||
|
||||
var dialog = Dialogs.showModalDialogUsingTemplate(Mustache.render(openDialog, data), false);
|
||||
|
||||
var $dialog = $(".makedrive.instance");
|
||||
|
||||
initializeEventHandlers($dialog);
|
||||
|
||||
var $container = $dialog.find('.open-files-container');
|
||||
|
||||
var jstree = $container.jstree({
|
||||
plugins: ["ui", "themes", "json_data", "crrm"],
|
||||
json_data: {
|
||||
data: fileTreeDataProvider,
|
||||
correct_state: false },
|
||||
core: {
|
||||
html_titles: true,
|
||||
animation: 0,
|
||||
strings : {
|
||||
loading : "Loading",
|
||||
new_node : "New node"
|
||||
}
|
||||
},
|
||||
themes: {
|
||||
theme: "brackets",
|
||||
url: "styles/jsTreeTheme.css",
|
||||
dots: false,
|
||||
icons: false
|
||||
}
|
||||
});
|
||||
|
||||
jstree.on('dblclick.jstree', handleFileDoubleClick);
|
||||
}
|
||||
|
||||
exports.showOpenDialog = showOpenDialog;
|
||||
|
||||
});
|
|
@ -46,6 +46,7 @@
|
|||
|
||||
<!-- Pre-load third party scripts that cannot be async loaded. -->
|
||||
<!-- build:js thirdparty/thirdparty.min.js -->
|
||||
<script src="thirdparty/browser-appshell.js"></script>
|
||||
<script src="thirdparty/less-config.js"></script>
|
||||
<script src="thirdparty/less-1.7.5.min.js"></script>
|
||||
<script src="thirdparty/mustache/mustache.js"></script>
|
||||
|
|
|
@ -35,7 +35,7 @@ require.config({
|
|||
|
||||
// The file system implementation. Change this value to use different
|
||||
// implementations (e.g. cloud-based storage).
|
||||
"fileSystemImpl" : "filesystem/impls/appshell/AppshellFileSystem"
|
||||
"fileSystemImpl" : "filesystem/impls/makedrive/MakeDriveFileSystem"
|
||||
},
|
||||
map: {
|
||||
"*": {
|
||||
|
|
|
@ -640,7 +640,9 @@ define(function (require, exports, module) {
|
|||
* @return {!string} fullPath reference
|
||||
*/
|
||||
function _getWelcomeProjectPath() {
|
||||
return ProjectModel._getWelcomeProjectPath(Urls.GETTING_STARTED, FileUtils.getNativeBracketsDirectoryPath());
|
||||
// XXXhumphd: just use root dir for now in browser
|
||||
return '/';
|
||||
//return ProjectModel._getWelcomeProjectPath(Urls.GETTING_STARTED, FileUtils.getNativeBracketsDirectoryPath());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -729,7 +731,9 @@ define(function (require, exports, module) {
|
|||
* first launch.
|
||||
*/
|
||||
function getInitialProjectPath() {
|
||||
return updateWelcomeProjectPath(PreferencesManager.getViewState("projectPath"));
|
||||
// XXXhumphd: just use root dir for now in browser
|
||||
return '/';
|
||||
//return updateWelcomeProjectPath(PreferencesManager.getViewState("projectPath"));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -889,73 +893,71 @@ define(function (require, exports, module) {
|
|||
PreferencesManager._stateProjectLayer.setProjectPath(rootPath);
|
||||
}
|
||||
|
||||
// Populate file tree as long as we aren't running in the browser
|
||||
if (!brackets.inBrowser) {
|
||||
if (!isUpdating) {
|
||||
_watchProjectRoot(rootPath);
|
||||
}
|
||||
// Point at a real folder structure on local disk
|
||||
var rootEntry = FileSystem.getDirectoryForPath(rootPath);
|
||||
rootEntry.exists(function (err, exists) {
|
||||
if (exists) {
|
||||
var projectRootChanged = (!model.projectRoot || !rootEntry) ||
|
||||
model.projectRoot.fullPath !== rootEntry.fullPath;
|
||||
// Populate file tree
|
||||
if (!isUpdating) {
|
||||
_watchProjectRoot(rootPath);
|
||||
}
|
||||
// Point at a real folder structure on local disk
|
||||
var rootEntry = FileSystem.getDirectoryForPath(rootPath);
|
||||
rootEntry.exists(function (err, exists) {
|
||||
if (exists) {
|
||||
var projectRootChanged = (!model.projectRoot || !rootEntry) ||
|
||||
model.projectRoot.fullPath !== rootEntry.fullPath;
|
||||
|
||||
// Success!
|
||||
var perfTimerName = PerfUtils.markStart("Load Project: " + rootPath);
|
||||
// Success!
|
||||
var perfTimerName = PerfUtils.markStart("Load Project: " + rootPath);
|
||||
|
||||
_projectWarnedForTooManyFiles = false;
|
||||
|
||||
_setProjectRoot(rootEntry).always(function () {
|
||||
model.setBaseUrl(PreferencesManager.getViewState("project.baseUrl", context) || "");
|
||||
_projectWarnedForTooManyFiles = false;
|
||||
|
||||
_setProjectRoot(rootEntry).always(function () {
|
||||
model.setBaseUrl(PreferencesManager.getViewState("project.baseUrl", context) || "");
|
||||
|
||||
if (projectRootChanged) {
|
||||
_reloadProjectPreferencesScope();
|
||||
PreferencesManager._setCurrentFile(rootPath);
|
||||
}
|
||||
if (projectRootChanged) {
|
||||
_reloadProjectPreferencesScope();
|
||||
PreferencesManager._setCurrentFile(rootPath);
|
||||
}
|
||||
|
||||
// If this is the most current welcome project, record it. In future launches, we want
|
||||
// to substitute the latest welcome project from the current build instead of using an
|
||||
// outdated one (when loading recent projects or the last opened project).
|
||||
if (rootPath === _getWelcomeProjectPath()) {
|
||||
addWelcomeProjectPath(rootPath);
|
||||
}
|
||||
// If this is the most current welcome project, record it. In future launches, we want
|
||||
// to substitute the latest welcome project from the current build instead of using an
|
||||
// outdated one (when loading recent projects or the last opened project).
|
||||
if (rootPath === _getWelcomeProjectPath()) {
|
||||
addWelcomeProjectPath(rootPath);
|
||||
}
|
||||
|
||||
if (projectRootChanged) {
|
||||
// Allow asynchronous event handlers to finish before resolving result by collecting promises from them
|
||||
exports.trigger("projectOpen", model.projectRoot);
|
||||
result.resolve();
|
||||
} else {
|
||||
exports.trigger("projectRefresh", model.projectRoot);
|
||||
result.resolve();
|
||||
}
|
||||
PerfUtils.addMeasurement(perfTimerName);
|
||||
});
|
||||
} else {
|
||||
console.log("error loading project");
|
||||
_showErrorDialog(ERR_TYPE_LOADING_PROJECT_NATIVE, true, err || FileSystemError.NOT_FOUND, rootPath)
|
||||
.done(function () {
|
||||
// Reset _projectRoot to null so that the following _loadProject call won't
|
||||
// run the 'beforeProjectClose' event a second time on the original project,
|
||||
// which is now partially torn down (see #6574).
|
||||
model.projectRoot = null;
|
||||
if (projectRootChanged) {
|
||||
// Allow asynchronous event handlers to finish before resolving result by collecting promises from them
|
||||
exports.trigger("projectOpen", model.projectRoot);
|
||||
result.resolve();
|
||||
} else {
|
||||
exports.trigger("projectRefresh", model.projectRoot);
|
||||
result.resolve();
|
||||
}
|
||||
PerfUtils.addMeasurement(perfTimerName);
|
||||
});
|
||||
} else {
|
||||
console.log("error loading project");
|
||||
_showErrorDialog(ERR_TYPE_LOADING_PROJECT_NATIVE, true, err || FileSystemError.NOT_FOUND, rootPath)
|
||||
.done(function () {
|
||||
// Reset _projectRoot to null so that the following _loadProject call won't
|
||||
// run the 'beforeProjectClose' event a second time on the original project,
|
||||
// which is now partially torn down (see #6574).
|
||||
model.projectRoot = null;
|
||||
|
||||
// The project folder stored in preference doesn't exist, so load the default
|
||||
// project directory.
|
||||
// TODO (issue #267): When Brackets supports having no project directory
|
||||
// defined this code will need to change
|
||||
_getFallbackProjectPath().done(function (path) {
|
||||
_loadProject(path).always(function () {
|
||||
// Make sure not to reject the original deferred until the fallback
|
||||
// project is loaded, so we don't violate expectations that there is always
|
||||
// a current project before continuing after _loadProject().
|
||||
result.reject();
|
||||
});
|
||||
// The project folder stored in preference doesn't exist, so load the default
|
||||
// project directory.
|
||||
// TODO (issue #267): When Brackets supports having no project directory
|
||||
// defined this code will need to change
|
||||
_getFallbackProjectPath().done(function (path) {
|
||||
_loadProject(path).always(function () {
|
||||
// Make sure not to reject the original deferred until the fallback
|
||||
// project is loaded, so we don't violate expectations that there is always
|
||||
// a current project before continuing after _loadProject().
|
||||
result.reject();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return result.promise();
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
/**
|
||||
* Browser shim for Brackets AppShell API. See:
|
||||
* https://github.com/adobe/brackets-shell
|
||||
* https://github.com/adobe/brackets-shell/blob/adf27a65d501fae71cc6d8905d6dc3d9460208a9/appshell/appshell_extensions.js
|
||||
* http://www.hyperlounge.com/blog/hacking-the-brackets-shell/
|
||||
*/
|
||||
|
||||
(function(global, navigator) {
|
||||
|
||||
var startupTime = Date.now();
|
||||
function getElapsedMilliseconds() {
|
||||
return (Date.now()) - startupTime;
|
||||
}
|
||||
|
||||
function functionNotImplemented() {
|
||||
throw "Function not (yet) implemented in browser-appshell.";
|
||||
}
|
||||
|
||||
// Provide an implementation of appshell.app as expected by src/utils/Global.js
|
||||
// and other parts of the code.
|
||||
var appshell = global.appshell = global.appshell || {};
|
||||
|
||||
appshell.app = {
|
||||
ERR_NODE_FAILED: -3,
|
||||
ERR_NODE_NOT_YET_STARTED: -1,
|
||||
ERR_NODE_PORT_NOT_YET_SET: -2,
|
||||
NO_ERROR: 0,
|
||||
|
||||
// TODO: deal with getter *and* setter
|
||||
language: navigator.language,
|
||||
|
||||
abortQuit: function() {
|
||||
functionNotImplemented();
|
||||
},
|
||||
addMenu: function(title, id, position, relativeId, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
addMenuItem: function(parentId, title, id, key, displayStr, position, relativeId, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
closeLiveBrowser: function(callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
dragWindow: function() {
|
||||
functionNotImplemented();
|
||||
},
|
||||
getApplicationSupportDirectory: function() {
|
||||
return '/ApplicationSupport';
|
||||
},
|
||||
getDroppedFiles: function(callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
getElapsedMilliseconds: getElapsedMilliseconds,
|
||||
getMenuItemState: function(commandid, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
getMenuPosition: function(commandId, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
getMenuTitle: function(commandid, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
getPendingFilesToOpen: function(callback) {
|
||||
// No files are passed to the app on startup, unless we want to support
|
||||
// URL based params with a list. For now return an empty array.
|
||||
callback(null, []);
|
||||
},
|
||||
getRemoteDebuggingPort: function() {
|
||||
functionNotImplemented();
|
||||
},
|
||||
getUserDocumentsDirectory: function() {
|
||||
functionNotImplemented();
|
||||
},
|
||||
openLiveBrowser: function(url, enableRemoteDebugging, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
openURLInDefaultBrowser: function(url, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
quit: function() {
|
||||
functionNotImplemented();
|
||||
},
|
||||
removeMenu: function(commandId, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
removeMenuItem: function(commandId, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
setMenuItemShortcut: function(commandId, shortcut, displayStr, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
setMenuItemState: function(commandid, enabled, checked, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
setMenuTitle: function(commandid, title, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
showDeveloperTools: function() {
|
||||
functionNotImplemented();
|
||||
},
|
||||
showExtensionsFolder: function(appURL, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
showOSFolder: function(path, callback) {
|
||||
functionNotImplemented();
|
||||
},
|
||||
|
||||
// https://github.com/adobe/brackets-shell/blob/959836be7a3097e2ea3298729ebd616247c83dce/appshell/appshell_node_process.h#L30
|
||||
getNodeState: function(callback) {
|
||||
callback(/* BRACKETS_NODE_FAILED = -3 */ -3);
|
||||
}
|
||||
};
|
||||
|
||||
global.brackets = appshell;
|
||||
|
||||
}(window, window.navigator));
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 3ab4cdbc14a44c0221d4e58e59697a5847c3e6c9
|
|
@ -391,6 +391,42 @@ define(function (require, exports, module) {
|
|||
return new $.Deferred().resolve().promise();
|
||||
}
|
||||
|
||||
// Load *subset* of the usual builtin extensions list, and don't try to find any user/dev extensions
|
||||
if (brackets.inBrowser) {
|
||||
var basePath = PathUtils.directory(window.location.href) + "extensions/default/",
|
||||
defaultExtensions = [
|
||||
// Core extensions we want to support in the browser
|
||||
"CSSCodeHints",
|
||||
"HTMLCodeHints",
|
||||
"HtmlEntityCodeHints",
|
||||
"InlineColorEditor",
|
||||
"JavaScriptQuickEdit",
|
||||
"JSLint",
|
||||
"LESSSupport",
|
||||
"QuickOpenCSS",
|
||||
"QuickOpenHTML",
|
||||
"QuickOpenJavaScript",
|
||||
"QuickView",
|
||||
"RecentProjects",
|
||||
"UrlCodeHints",
|
||||
"WebPlatformDocs",
|
||||
|
||||
// Custom extensions we want loaded by default
|
||||
// NOTE: Maps to a folder inside /src/extensions/default/
|
||||
"makedrive-sync-icon"
|
||||
|
||||
// "ExampleExtension",
|
||||
];
|
||||
|
||||
return Async.doInParallel(defaultExtensions, function (item) {
|
||||
var extConfig = {
|
||||
baseUrl: basePath + item
|
||||
};
|
||||
return loadExtension(item, extConfig, "main");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (!paths) {
|
||||
params.parse();
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче