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.js 25f2a24cdf

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 for 8d6f548f72

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:
David Humphrey (:humph) david.humphrey@senecacollege.ca 2014-06-23 16:01:19 -04:00
Родитель eceb1d6a2f
Коммит fa5d187ff4
15 изменённых файлов: 1111 добавлений и 164 удалений

6
.gitmodules поставляемый
Просмотреть файл

@ -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
Просмотреть файл

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

116
src/thirdparty/browser-appshell.js поставляемый Normal file
Просмотреть файл

@ -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));

1
src/thirdparty/makedrive поставляемый Submodule

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