Skip to content

Commit

Permalink
Add bramble.showUploadFilesDialog() for https://github.com/mozilla/th…
Browse files Browse the repository at this point in the history
  • Loading branch information
humphd committed Jul 30, 2015
1 parent 02bfff5 commit 4677198
Show file tree
Hide file tree
Showing 15 changed files with 697 additions and 231 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ A standard set of default extensions are always turned on:
* brackets-paste-and-indent
* BrambleUrlCodeHints
* Autosave
* UploadFiles

You could disable QuickView and CSSCodeHints by loading Bramble with `?disableExtensions=QuickView,CSSCodeHints`
on the URL.
Expand Down Expand Up @@ -302,6 +303,7 @@ to be notified when the action completes:
* `disableWordWrap([callback])` - turns off word wrap for the editor
* `showTutorial([callback])` - shows tutorial (i.e., tutorial.html) vs editor contents in preview
* `hideTutorial([callback])` - stops showing tutorial (i.e., tutorial.html) and uses editor contents in preview
* `showUploadFilesDialog([callback])` - shows the Upload Files dialog, allowing users to drag-and-drop, upload a file, or take a selfie.

## Bramble Instance Events

Expand Down
4 changes: 4 additions & 0 deletions src/bramble/client/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -673,5 +673,9 @@ define([
this._executeRemoteCommand({commandCategory: "bramble", command: "BRAMBLE_HIDE_TUTORIAL"}, callback);
};

BrambleProxy.prototype.showUploadFilesDialog = function(callback) {
this._executeRemoteCommand({commandCategory: "bramble", command: "SHOW_UPLOAD_FILES_DIALOG"}, callback);
};

return Bramble;
});
3 changes: 0 additions & 3 deletions src/extensions/default/BrambleUrlCodeHints/camera/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,6 @@ define(function (require, exports, module) {
// do not support this functionality of HTML5
Camera.isSupported = !!getUserMedia;

// TODO: l10n
Camera.selfieLabel = "Take a Selfie...";

// Initiate the camera by requesting access to the user's webcam
Camera.prototype.start = function() {
var self = this;
Expand Down
43 changes: 7 additions & 36 deletions src/extensions/default/BrambleUrlCodeHints/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,10 @@ define(function (require, exports, module) {
ProjectManager = brackets.getModule("project/ProjectManager"),
ExtensionUtils = brackets.getModule("utils/ExtensionUtils"),
EditorManager = brackets.getModule("editor/EditorManager"),
StartupState = brackets.getModule("bramble/StartupState"),
Path = brackets.getModule("filesystem/impls/filer/FilerUtils").Path,
Content = brackets.getModule("filesystem/impls/filer/lib/content"),
Camera = require("camera/index"),
CameraDialog = require("camera-dialog"),
Selfie = require("selfie"),

Data = require("text!data.json"),

Expand Down Expand Up @@ -221,27 +220,11 @@ define(function (require, exports, module) {
}
});

var highestNumber = 0;
result.forEach(function (item){
item = item.split("/");
item = item[item.length-1];
if(item.indexOf("selfie") === 0) {
// Removes extension from filename
var fileNameParts = /selfie(\d*)\.png/.exec(item);

var currentNumber = fileNameParts && fileNameParts[1] ? Number(fileNameParts[1]) : 0;
if(currentNumber > highestNumber) {
highestNumber = currentNumber;
}
}
});

result.sort();

// Possibly adding the "Take Selfie" label to the bottom of results
if(isImage && Camera.isSupported) {
result.push(Camera.selfieLabel);
this.selfieFileName = "selfie" + (highestNumber + 1) + ".png";
result.push(Selfie.label);
}

return result;
Expand Down Expand Up @@ -572,9 +555,6 @@ define(function (require, exports, module) {
*/
BrambleUrlCodeHints.prototype.insertHint = function (completion) {
var that = this;
var cameraDialog;
var projectRoot;
var savePath;

function insert(text) {
var mode = that.editor.getModeForSelection();
Expand All @@ -591,28 +571,17 @@ define(function (require, exports, module) {
return false;
}

if (completion === Camera.selfieLabel) {
// NOTE: we need to deal with Brackets expecting a trailing / on dir names.
projectRoot = StartupState.project("root").replace(/\/?$/, "/");
savePath = Path.join(projectRoot, this.selfieFileName);

cameraDialog = new CameraDialog(savePath);
cameraDialog.show()
.done(function(selfieFilePath){
if (completion === Selfie.label) {
Selfie.takeSelfie()
.done(function(selfieFilePath) {
if(selfieFilePath) {
// Give back a path relative to the project's mount root.
selfieFilePath = FileUtils.getRelativeFilename(projectRoot,
selfieFilePath);
insert(selfieFilePath);
}
EditorManager.getActiveEditor().focus();
})
.fail(function(err) {
EditorManager.getActiveEditor().focus();
console.error("[Selfie error] ", err);
})
.always(function() {
cameraDialog.close();
});
return false;
}
Expand Down Expand Up @@ -900,4 +869,6 @@ define(function (require, exports, module) {
FileSystem.on("change", _clearCachedHints);
FileSystem.on("rename", _clearCachedHints);
});

Selfie.addSelfieCommand();
});
97 changes: 97 additions & 0 deletions src/extensions/default/BrambleUrlCodeHints/selfie.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, $ */

define(function (require, exports, module) {
"use strict";

var CommandManager = brackets.getModule("command/CommandManager");
var FileUtils = brackets.getModule("file/FileUtils");
var StartupState = brackets.getModule("bramble/StartupState");
var Filer = brackets.getModule("filesystem/impls/filer/BracketsFiler");
var Path = Filer.Path;
var fs = Filer.fs();

var CameraDialog = require("camera-dialog");

// TODO: l10n
var SELFIE_MENU_LABEL = "Take a Selfie...";
var SELFIE_FILENAME = "selfie";
var CMD_SELFIE_TEXT = "Take a Selfie";
var CMD_SELFIE_ID = "bramble.selfie";

/**
* Generate a unique filename for this selfie, taking into account
* that there might be other selfies already in the dir. Use an
* auto-increment on the filename (i.e., selfie1.png, selfie2.png)
*/
function _generateFilename(callback) {
var root = StartupState.project("root");
fs.readdir(root, function(err, entries) {
if(err) {
return callback(err);
}

var highest = 0;
entries.forEach(function(entry){
var filenameParts = /selfie(\d*)\.png/.exec(entry);
var current;

if(filenameParts) {
current = Number(filenameParts[1]);
if(current > highest) {
highest = current;
}
}
});

var filename = SELFIE_FILENAME + (highest + 1) + ".png";
callback(null, filename);
});
}

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

function showCamera(filename) {
// NOTE: we need to deal with Brackets expecting a trailing / on dir names.
var projectRoot = StartupState.project("root").replace(/\/?$/, "/");
var savePath = Path.join(projectRoot, filename);
var cameraDialog = new CameraDialog(savePath);

cameraDialog.show()
.done(function(selfieFilePath){
if(selfieFilePath) {
// Give back a path relative to the project's mount root.
selfieFilePath = FileUtils.getRelativeFilename(projectRoot,
selfieFilePath);
}
result.resolve(selfieFilePath);
})
.fail(function(err) {
result.reject(err);
})
.always(function() {
cameraDialog.close();
cameraDialog = null;
});
}

_generateFilename(function(err, filename) {
if(err) {
console.error("[Selfie error] ", err);
return result.reject();
}
showCamera(filename);
});

return result.promise();
}

function addSelfieCommand() {
CommandManager.register(CMD_SELFIE_TEXT, CMD_SELFIE_ID, takeSelfie);
}

exports.addSelfieCommand = addSelfieCommand;
exports.takeSelfie = takeSelfie;
exports.label = SELFIE_MENU_LABEL;
});
162 changes: 162 additions & 0 deletions src/extensions/default/UploadFiles/UploadFilesDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */
/*global define, brackets, $ */

define(function (require, exports, module) {
"use strict";

var StartupState = brackets.getModule("bramble/StartupState");
var Path = brackets.getModule("filesystem/impls/filer/BracketsFiler").Path;
var CommandManager = brackets.getModule("command/CommandManager");
var CMD_OPEN = brackets.getModule("command/Commands").CMD_OPEN;
var Dialogs = brackets.getModule("widgets/Dialogs");
var DragAndDrop = brackets.getModule("utils/DragAndDrop");
var KeyEvent = brackets.getModule("utils/KeyEvent");

var dialogHTML = require("text!htmlContent/upload-files-dialog.html");

function FileInput() {
$(document.body)
.append($('<input class="upload-files-input-elem" type="file" multiple />'));
}
FileInput.prototype.getFiles = function() {
return this.getElem$()[0].files;
};
FileInput.prototype.getElem$ = function() {
return $(".upload-files-input-elem");
};
FileInput.prototype.remove = function() {
this.getElem$().remove();
};


function FileUploadDialog() {
this.fileInput = new FileInput();
this.deferred = new $.Deferred();
}
FileUploadDialog.prototype.show = function() {
var self = this;
var deferred = self.deferred;

// We ignore the promise returned by showModalDialogUsingTemplate, since we're managing the
// lifecycle of the dialog ourselves.
Dialogs.showModalDialogUsingTemplate(Mustache.render(dialogHTML), false);

var $dlg = $(".upload-files-dialog.instance");
var $dragFilesAreaDiv = $dlg.find(".drag-files-area");
var $uploadFilesDiv = $dlg.find(".uploading-files");
var $fromComputerButton = $dlg.find(".dialog-button[data-button-id='from-computer']");
var $takeSelfieButton = $dlg.find(".dialog-button[data-button-id='take-selfie']");
var $cancelButton = $dlg.find(".dialog-button[data-button-id='cancel']");
var $dropZoneDiv = $dlg.find(".drop-zone");

// Hide the uploadingFiles div until a drop event
$uploadFilesDiv.hide();

$fromComputerButton.one("click", self._handleFromComputer.bind(self));
$takeSelfieButton.one("click", self._handleTakeSelfie.bind(self));
$cancelButton.one("click", function() {
self.hide();
self.destroy();
});
$(window.document.body).on("keyup.installDialog", self._handleKeyUp.bind(self));

// Hook up drag-and-drop handling
DragAndDrop.attachHandlers({
elem: $dropZoneDiv[0],
ondragover: function() {
if(self._dragover) {
return;
}
self._dragover = true;
$dragFilesAreaDiv.addClass("drag-over");
},
ondragleave: function() {
delete self._dragover;
$dragFilesAreaDiv.removeClass("drag-over");
},
ondrop: function() {
// Turn off the other buttons
$fromComputerButton.off("click", self._handleFromComputer.bind(self));
$takeSelfieButton.off("click", self._handleTakeSelfie.bind(self));

// Switch to the upload spinner
$dragFilesAreaDiv.hide();
$uploadFilesDiv.show();
},
onfilesdone: function() {
self.hide();
self.destroy();
},
autoRemoveHandlers: true
});

return deferred.promise();
};
FileUploadDialog.prototype._handleKeyUp = function(e) {
var self = this;

// Dismiss dialog on ESC
if (e.keyCode === KeyEvent.DOM_VK_ESCAPE) {
self.hide();
self.destroy();
self.deferred.resolve();
}
};
FileUploadDialog.prototype._handleTakeSelfie = function() {
var self = this;
var deferred = self.deferred;

self.hide();
self.destroy();

// Take a selfie, then show the image in the editor.
CommandManager.execute("bramble.selfie")
.done(function(filename) {
// Get the absolute path to the new file and open
filename = Path.join(StartupState.project("root"), filename);
CommandManager.execute(CMD_OPEN, { fullPath: filename })
.then(deferred.resolve, deferred.reject);
})
.fail(deferred.reject);
};
FileUploadDialog.prototype._handleFromComputer = function() {
var self = this;
var deferred = self.deferred;

self.hide();

function _processFiles(e) {
var files = self.fileInput.getFiles();
DragAndDrop.processFiles(files, function(err) {
self.destroy();

if(err) {
deferred.reject();
} else {
deferred.resolve();
}
});
}

// Trigger the <input type="file"> added previously to show and process files.
var input = self.fileInput.getElem$();
input.on("change", _processFiles);
input.click();
};
FileUploadDialog.prototype.hide = function() {
var self = this;
$(window.document.body).off("keyup.installDialog", self._handleKeyUp);
Dialogs.cancelModalDialogIfOpen("upload-files-dialog");
};
FileUploadDialog.prototype.destroy = function() {
this.fileInput.remove();
};


function show() {
var uploadDialog = new FileUploadDialog();
return uploadDialog.show();
}

exports.show = show;
});
Loading

0 comments on commit 4677198

Please sign in to comment.