diff --git a/build/bundle.js b/build/bundle.js
index 37c3d296..d4894cc6 100644
--- a/build/bundle.js
+++ b/build/bundle.js
@@ -8614,6 +8614,7 @@ require.define("/src/js/views/index.js",function(require,module,exports,__dirnam
var _ = require('underscore');
// horrible hack to get localStorage Backbone plugin
var Backbone = (!require('../util').isBrowser()) ? require('backbone') : window.Backbone;
+var Main = require('../app');
var BaseView = Backbone.View.extend({
getDestination: function() {
@@ -8724,6 +8725,7 @@ var ModalView = Backbone.View.extend({
initialize: function(options) {
this.render();
+ this.stealKeyboard();
},
render: function() {
@@ -8732,6 +8734,25 @@ var ModalView = Backbone.View.extend({
$('body').append(this.el);
},
+ stealKeyboard: function() {
+ console.warn('stealing keyboard');
+ Main.getEventBaton().stealBaton('keydown', this.onKeyDown, this);
+ Main.getEventBaton().stealBaton('keyup', this.onKeyUp, this);
+ },
+
+ releaseKeyboard: function() {
+ Main.getEventBaton().releaseBaton('keydown', this.onKeyDown, this);
+ Main.getEventBaton().releaseBaton('keyup', this.onKeyUp, this);
+ },
+
+ onKeyDown: function(e) {
+ e.preventDefault();
+ },
+
+ onKeyUp: function(e) {
+ e.preventDefault();
+ },
+
show: function() {
this.toggleZ(true);
this.toggleShow(true);
@@ -8762,6 +8783,7 @@ var ModalView = Backbone.View.extend({
tearDown: function() {
this.$el.html('');
$('body')[0].removeChild(this.el);
+ this.releaseKeyboard();
}
});
@@ -8823,6 +8845,1077 @@ exports.ConfirmCancelView = ConfirmCancelView;
exports.LeftRightView = LeftRightView;
+});
+
+require.define("/src/js/app/index.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+var Backbone = require('backbone');
+
+/**
+ * Globals
+ */
+var events = _.clone(Backbone.Events);
+var ui;
+var mainVis;
+var eventBaton;
+
+///////////////////////////////////////////////////////////////////////
+
+var init = function(){
+ var Visualization = require('../visuals/visualization').Visualization;
+ var EventBaton = require('../util/eventBaton').EventBaton;
+
+ eventBaton = new EventBaton();
+ ui = new UI();
+ mainVis = new Visualization({
+ el: $('#canvasWrapper')[0]
+ });
+
+ // we always want to focus the text area to collect input
+ var focusTextArea = function() {
+ console.log('focusing text area');
+ $('#commandTextField').focus();
+ };
+ focusTextArea();
+
+ $(window).focus(focusTextArea);
+ $(document).click(focusTextArea);
+
+ // but when the input is fired in the text area, we pipe that to whoever is
+ // listenining
+ var makeKeyListener = function(name) {
+ return function() {
+ var args = [name];
+ _.each(arguments, function(arg) {
+ args.push(arg);
+ });
+ eventBaton.trigger.apply(eventBaton, args);
+ };
+ };
+
+ $('#commandTextField').on('keydown', makeKeyListener('keydown'));
+ $('#commandTextField').on('keyup', makeKeyListener('keyup'));
+
+ /* hacky demo functionality */
+ if (/\?demo/.test(window.location.href)) {
+ setTimeout(function() {
+ events.trigger('submitCommandValueFromEvent', "gc; git checkout HEAD~1; git commit; git checkout -b bugFix; gc; gc; git rebase -i HEAD~2; git rebase master; git checkout master; gc; gc; git merge bugFix");
+ }, 500);
+ }
+};
+
+$(document).ready(init);
+
+function UI() {
+ this.active = true;
+ var Collections = require('../models/collections');
+ var CommandViews = require('../views/commandViews');
+
+ this.commandCollection = new Collections.CommandCollection();
+ this.commandBuffer = new Collections.CommandBuffer({
+ collection: this.commandCollection
+ });
+
+ this.commandPromptView = new CommandViews.CommandPromptView({
+ el: $('#commandLineBar'),
+ collection: this.commandCollection
+ });
+ this.commandLineHistoryView = new CommandViews.CommandLineHistoryView({
+ el: $('#commandLineHistory'),
+ collection: this.commandCollection
+ });
+
+ $('#commandTextField').focus();
+ eventBaton.stealBaton('windowFocus', this.onWindowFocus, this);
+}
+
+UI.prototype.onWindowFocus = function() {
+ this.commandPromptView.focus();
+};
+
+exports.getEvents = function() {
+ return events;
+};
+
+exports.getUI = function() {
+ return ui;
+};
+
+exports.getMainVis = function() {
+ return mainVis;
+};
+
+exports.getEventBaton = function() {
+ return eventBaton;
+};
+
+exports.init = init;
+
+
+});
+
+require.define("/src/js/util/eventBaton.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+
+function EventBaton() {
+ this.eventMap = {};
+}
+
+// this method steals the "baton" -- aka, only this method will now
+// get called. analogous to events.on
+// EventBaton.prototype.on = function(name, func, context) {
+EventBaton.prototype.stealBaton = function(name, func, context) {
+ if (!name) { throw new Error('need name'); }
+
+ var listeners = this.eventMap[name] || [];
+ listeners.push({
+ func: func,
+ context: context
+ });
+ this.eventMap[name] = listeners;
+};
+
+EventBaton.prototype.trigger = function(name) {
+ // arguments is weird and doesnt do slice right
+ var argsToApply = [];
+ for (var i = 1; i < arguments.length; i++) {
+ argsToApply.push(arguments[i]);
+ }
+
+ var listeners = this.eventMap[name];
+ if (!listeners) {
+ console.warn('no listeners for', name);
+ return;
+ }
+ // call the top most listener with context and such
+ var toCall = listeners.slice(-1)[0];
+ toCall.func.apply(toCall.context, argsToApply);
+};
+
+EventBaton.prototype.releaseBaton = function(name, func, context) {
+ if (!name) { throw new Error('need name'); }
+ // might be in the middle of the stack
+ var listeners = this.eventMap[name];
+ if (!listeners || !listeners.length) {
+ throw new Error('no one has that baton!' + name);
+ }
+
+ var newListeners = [];
+ var found = false;
+ _.each(listeners, function(listenerObj) {
+ if (listenerObj.func === func) {
+ found = true;
+ } else {
+ newListeners.push(listenerObj);
+ }
+ }, this);
+
+ if (!found) {
+ throw new Error('did not find that function');
+ }
+ this.eventMap[name] = newListeners;
+};
+
+exports.EventBaton = EventBaton;
+
+
+});
+
+require.define("/src/js/views/commandViews.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+// horrible hack to get localStorage Backbone plugin
+var Backbone = (!require('../util').isBrowser()) ? Backbone = require('backbone') : Backbone = window.Backbone;
+
+var CommandEntryCollection = require('../models/collections').CommandEntryCollection;
+var Main = require('../app');
+var Command = require('../models/commandModel').Command;
+var CommandEntry = require('../models/commandModel').CommandEntry;
+
+var Errors = require('../util/errors');
+var Warning = Errors.Warning;
+
+var util = require('../util');
+var keyboard = require('../util/keyboard');
+
+var CommandPromptView = Backbone.View.extend({
+ initialize: function(options) {
+ this.collection = options.collection;
+
+ // uses local storage
+ this.commands = new CommandEntryCollection();
+ this.commands.fetch({
+ success: _.bind(function() {
+ // reverse the commands. this is ugly but needs to be done...
+ var commands = [];
+ this.commands.each(function(c) {
+ commands.push(c);
+ });
+
+ commands.reverse();
+ this.commands.reset();
+
+ _.each(commands, function(c) {
+ this.commands.add(c);
+ }, this);
+ }, this)
+ });
+
+ this.index = -1;
+ this.commandSpan = this.$('#prompt span.command')[0];
+ this.commandCursor = this.$('#prompt span.cursor')[0];
+ this.focus();
+
+ Main.getEvents().on('processCommandFromEvent', this.addToCollection, this);
+ Main.getEvents().on('submitCommandValueFromEvent', this.submitValue, this);
+ Main.getEvents().on('rollupCommands', this.rollupCommands, this);
+
+ Main.getEventBaton().stealBaton('keydown', this.onKeyDown, this);
+ Main.getEventBaton().stealBaton('keyup', this.onKeyUp, this);
+ },
+
+ events: {
+ 'blur #commandTextField': 'hideCursor',
+ 'focus #commandTextField': 'showCursor'
+ },
+
+ blur: function() {
+ this.hideCursor();
+ },
+
+ focus: function() {
+ this.$('#commandTextField').focus();
+ this.showCursor();
+ },
+
+ hideCursor: function() {
+ this.toggleCursor(false);
+ },
+
+ showCursor: function() {
+ this.toggleCursor(true);
+ },
+
+ toggleCursor: function(state) {
+ $(this.commandCursor).toggleClass('shown', state);
+ },
+
+ onKeyDown: function(e) {
+ var el = e.srcElement;
+ this.updatePrompt(el);
+ },
+
+ onKeyUp: function(e) {
+ this.onKeyDown(e);
+
+ // we need to capture some of these events.
+ var keyToFuncMap = {
+ enter: _.bind(function() {
+ this.submit();
+ }, this),
+ up: _.bind(function() {
+ this.commandSelectChange(1);
+ }, this),
+ down: _.bind(function() {
+ this.commandSelectChange(-1);
+ }, this)
+ };
+
+ var key = keyboard.mapKeycodeToKey(e.which);
+ if (keyToFuncMap[key] !== undefined) {
+ e.preventDefault();
+ keyToFuncMap[key]();
+ this.onKeyDown(e);
+ }
+ },
+
+ badHtmlEncode: function(text) {
+ return text.replace(/&/g,'&')
+ .replace(/= this.commands.length || this.index < 0) {
+ this.clear();
+ this.index = -1;
+ return;
+ }
+
+ // yay! we actually can display something
+ var commandEntry = this.commands.toArray()[this.index].get('text');
+ this.setTextField(commandEntry);
+ },
+
+ clearLocalStorage: function() {
+ this.commands.each(function(c) {
+ Backbone.sync('delete', c, function() { });
+ }, this);
+ localStorage.setItem('CommandEntries', '');
+ },
+
+ setTextField: function(value) {
+ this.$('#commandTextField').val(value);
+ },
+
+ clear: function() {
+ this.setTextField('');
+ },
+
+ submit: function() {
+ var value = this.$('#commandTextField').val().replace('\n', '');
+ this.clear();
+ this.submitValue(value);
+ },
+
+ rollupCommands: function(numBack) {
+ var which = this.commands.toArray().slice(1, Number(numBack) + 1);
+ which.reverse();
+
+ var str = '';
+ _.each(which, function(commandEntry) {
+ str += commandEntry.get('text') + ';';
+ }, this);
+
+ console.log('the str', str);
+
+ var rolled = new CommandEntry({text: str});
+ this.commands.unshift(rolled);
+ Backbone.sync('create', rolled, function() { });
+ },
+
+ submitValue: function(value) {
+ // we should add the command to our local storage history
+ // if it's not a blank line and this is a new command...
+ // or if we edited the command in place
+ var shouldAdd = (value.length && this.index == -1) ||
+ ((value.length && this.index !== -1 &&
+ this.commands.toArray()[this.index].get('text') !== value));
+
+ if (shouldAdd) {
+ var commandEntry = new CommandEntry({text: value});
+ this.commands.unshift(commandEntry);
+
+ // store to local storage
+ Backbone.sync('create', commandEntry, function() { });
+
+ // if our length is too egregious, reset
+ if (this.commands.length > 100) {
+ this.clearLocalStorage();
+ }
+ }
+ this.index = -1;
+
+ util.splitTextCommand(value, function(command) {
+ this.addToCollection(command);
+ }, this);
+ },
+
+ addToCollection: function(value) {
+ var command = new Command({
+ rawStr: value
+ });
+ this.collection.add(command);
+ }
+});
+
+// This is the view for all commands -- it will represent
+// their status (inqueue, processing, finished, error),
+// their value ("git commit --amend"),
+// and the result (either errors or warnings or whatever)
+var CommandView = Backbone.View.extend({
+ tagName: 'div',
+ model: Command,
+ template: _.template($('#command-template').html()),
+
+ events: {
+ 'click': 'clicked'
+ },
+
+ clicked: function(e) {
+ },
+
+ initialize: function() {
+ this.model.bind('change', this.wasChanged, this);
+ this.model.bind('destroy', this.remove, this);
+ },
+
+ wasChanged: function(model, changeEvent) {
+ // for changes that are just comestic, we actually only want to toggle classes
+ // with jquery rather than brutally delete a html. doing so allows us
+ // to nicely fade things
+ var changes = changeEvent.changes;
+ var changeKeys = _.keys(changes);
+ if (_.difference(changeKeys, ['status']).length === 0) {
+ this.updateStatus();
+ } else {
+ this.render();
+ }
+ },
+
+ updateStatus: function() {
+ var statuses = ['inqueue', 'processing', 'finished'];
+ var toggleMap = {};
+ _.each(statuses, function(status) {
+ toggleMap[status] = false;
+ });
+ toggleMap[this.model.get('status')] = true;
+
+ var query = this.$('p.commandLine');
+
+ _.each(toggleMap, function(value, key) {
+ query.toggleClass(key, value);
+ });
+ },
+
+ render: function() {
+ var json = _.extend(
+ {
+ resultType: '',
+ result: '',
+ formattedWarnings: this.model.getFormattedWarnings()
+ },
+ this.model.toJSON()
+ );
+
+ this.$el.html(this.template(json));
+ return this;
+ },
+
+ remove: function() {
+ $(this.el).hide();
+ }
+});
+
+
+var CommandLineHistoryView = Backbone.View.extend({
+ initialize: function(options) {
+ this.collection = options.collection;
+
+ this.collection.on('add', this.addOne, this);
+ this.collection.on('reset', this.addAll, this);
+ this.collection.on('all', this.render, this);
+
+ this.collection.on('change', this.scrollDown, this);
+
+ Main.getEvents().on('issueWarning', this.addWarning, this);
+ Main.getEvents().on('commandScrollDown', this.scrollDown, this);
+ },
+
+ addWarning: function(msg) {
+ var err = new Warning({
+ msg: msg
+ });
+
+ var command = new Command({
+ error: err,
+ rawStr: 'Warning:'
+ });
+
+ this.collection.add(command);
+ },
+
+ scrollDown: function() {
+ // if commandDisplay is ever bigger than #terminal, we need to
+ // add overflow-y to terminal and scroll down
+ var cD = $('#commandDisplay')[0];
+ var t = $('#terminal')[0];
+
+ if ($(t).hasClass('scrolling')) {
+ t.scrollTop = t.scrollHeight;
+ return;
+ }
+ if (cD.clientHeight > t.clientHeight) {
+ $(t).css('overflow-y', 'scroll');
+ $(t).css('overflow-x', 'hidden');
+ $(t).addClass('scrolling');
+ t.scrollTop = t.scrollHeight;
+ }
+ },
+
+ addOne: function(command) {
+ var view = new CommandView({
+ model: command
+ });
+ this.$('#commandDisplay').append(view.render().el);
+ this.scrollDown();
+ },
+
+ addAll: function() {
+ this.collection.each(this.addOne);
+ }
+});
+
+exports.CommandPromptView = CommandPromptView;
+exports.CommandLineHistoryView = CommandLineHistoryView;
+
+});
+
+require.define("/src/js/models/commandModel.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+// horrible hack to get localStorage Backbone plugin
+var Backbone = (!require('../util').isBrowser()) ? Backbone = require('backbone') : Backbone = window.Backbone;
+
+var Errors = require('../util/errors');
+var GitCommands = require('../git/commands');
+var GitOptionParser = GitCommands.GitOptionParser;
+
+var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall;
+
+var CommandProcessError = Errors.CommandProcessError;
+var GitError = Errors.GitError;
+var Warning = Errors.Warning;
+var CommandResult = Errors.CommandResult;
+
+var Command = Backbone.Model.extend({
+ defaults: {
+ status: 'inqueue',
+ rawStr: null,
+ result: '',
+ createTime: null,
+
+ error: null,
+ warnings: null,
+ parseWaterfall: new ParseWaterfall(),
+
+ generalArgs: null,
+ supportedMap: null,
+ options: null,
+ method: null
+
+ },
+
+ initialize: function(options) {
+ this.initDefaults();
+ this.validateAtInit();
+
+ this.on('change:error', this.errorChanged, this);
+ // catch errors on init
+ if (this.get('error')) {
+ this.errorChanged();
+ }
+
+ this.parseOrCatch();
+ },
+
+ initDefaults: function() {
+ // weird things happen with defaults if you dont
+ // make new objects
+ this.set('generalArgs', []);
+ this.set('supportedMap', {});
+ this.set('warnings', []);
+ },
+
+ validateAtInit: function() {
+ if (this.get('rawStr') === null) {
+ throw new Error('Give me a string!');
+ }
+ if (!this.get('createTime')) {
+ this.set('createTime', new Date().toString());
+ }
+ },
+
+ setResult: function(msg) {
+ this.set('result', msg);
+ },
+
+ addWarning: function(msg) {
+ this.get('warnings').push(msg);
+ // change numWarnings so the change event fires. This is bizarre -- Backbone can't
+ // detect if an array changes, so adding an element does nothing
+ this.set('numWarnings', this.get('numWarnings') ? this.get('numWarnings') + 1 : 1);
+ },
+
+ getFormattedWarnings: function() {
+ if (!this.get('warnings').length) {
+ return '';
+ }
+ var i = '';
+ return '
' + i + this.get('warnings').join('
' + i) + '
';
+ },
+
+ parseOrCatch: function() {
+ this.expandShortcuts(this.get('rawStr'));
+ try {
+ this.processInstants();
+ } catch (err) {
+ Errors.filterError(err);
+ // errorChanged() will handle status and all of that
+ this.set('error', err);
+ return;
+ }
+
+ if (this.parseAll()) {
+ // something in our parse waterfall succeeded
+ return;
+ }
+
+ // if we reach here, this command is not supported :-/
+ this.set('error', new CommandProcessError({
+ msg: 'The command "' + this.get('rawStr') + '" isn\'t supported, sorry!'
+ })
+ );
+ },
+
+ errorChanged: function() {
+ var err = this.get('error');
+ if (err instanceof CommandProcessError ||
+ err instanceof GitError) {
+ this.set('status', 'error');
+ } else if (err instanceof CommandResult) {
+ this.set('status', 'finished');
+ } else if (err instanceof Warning) {
+ this.set('status', 'warning');
+ }
+ this.formatError();
+ },
+
+ formatError: function() {
+ this.set('result', this.get('error').toResult());
+ },
+
+ expandShortcuts: function(str) {
+ str = this.get('parseWaterfall').expandAllShortcuts(str);
+ this.set('rawStr', str);
+ },
+
+ processInstants: function() {
+ var str = this.get('rawStr');
+ // first if the string is empty, they just want a blank line
+ if (!str.length) {
+ throw new CommandResult({msg: ""});
+ }
+
+ // then instant commands that will throw
+ this.get('parseWaterfall').processAllInstants(str);
+ },
+
+ parseAll: function() {
+ var str = this.get('rawStr');
+ var results = this.get('parseWaterfall').parseAll(str);
+
+ if (!results) {
+ // nothing parsed successfully
+ return false;
+ }
+
+ _.each(results.toSet, function(obj, key) {
+ this.set(key, obj);
+ }, this);
+ return true;
+ }
+});
+
+// command entry is for the commandview
+var CommandEntry = Backbone.Model.extend({
+ defaults: {
+ text: ''
+ },
+ // stub out if no plugin available
+ localStorage: (Backbone.LocalStorage) ? new Backbone.LocalStorage('CommandEntries') : null
+});
+
+exports.CommandEntry = CommandEntry;
+exports.Command = Command;
+
+});
+
+require.define("/src/js/git/commands.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+
+var Errors = require('../util/errors');
+var CommandProcessError = Errors.CommandProcessError;
+var GitError = Errors.GitError;
+var Warning = Errors.Warning;
+var CommandResult = Errors.CommandResult;
+
+var shortcutMap = {
+ 'git commit': /^gc($|\s)/,
+ 'git add': /^ga($|\s)/,
+ 'git checkout': /^go($|\s)/,
+ 'git rebase': /^gr($|\s)/,
+ 'git branch': /^gb($|\s)/,
+ 'git status': /^gs($|\s)/,
+ 'git help': /^git$/
+};
+
+var instantCommands = [
+ [/^git help($|\s)/, function() {
+ var lines = [
+ 'Git Version PCOTTLE.1.0',
+ '
',
+ 'Usage:',
+ _.escape('\t git []'),
+ '
',
+ 'Supported commands:',
+ '
'
+ ];
+ var commands = GitOptionParser.prototype.getMasterOptionMap();
+
+ // build up a nice display of what we support
+ _.each(commands, function(commandOptions, command) {
+ lines.push('git ' + command);
+ _.each(commandOptions, function(vals, optionName) {
+ lines.push('\t ' + optionName);
+ }, this);
+ }, this);
+
+ // format and throw
+ var msg = lines.join('\n');
+ msg = msg.replace(/\t/g, ' ');
+ throw new CommandResult({
+ msg: msg
+ });
+ }]
+];
+
+var regexMap = {
+ // ($|\s) means that we either have to end the string
+ // after the command or there needs to be a space for options
+ commit: /^commit($|\s)/,
+ add: /^add($|\s)/,
+ checkout: /^checkout($|\s)/,
+ rebase: /^rebase($|\s)/,
+ reset: /^reset($|\s)/,
+ branch: /^branch($|\s)/,
+ revert: /^revert($|\s)/,
+ log: /^log($|\s)/,
+ merge: /^merge($|\s)/,
+ show: /^show($|\s)/,
+ status: /^status($|\s)/,
+ 'cherry-pick': /^cherry-pick($|\s)/
+};
+
+var parse = function(str) {
+ // now slice off command part
+ var fullCommand = str.slice('git '.length);
+ var method;
+ var options;
+
+ // see if we support this particular command
+ _.each(regexMap, function(regex, thisMethod) {
+ if (regex.exec(fullCommand)) {
+ options = fullCommand.slice(thisMethod.length + 1);
+ method = thisMethod;
+ }
+ }, this);
+
+ if (!method) {
+ return false;
+ }
+
+ // we support this command!
+ // parse off the options and assemble the map / general args
+ var parsedOptions = new GitOptionParser(method, options);
+ return {
+ toSet: {
+ generalArgs: parsedOptions.generalArgs,
+ supportedMap: parsedOptions.supportedMap,
+ method: method,
+ options: options
+ }
+ };
+};
+
+/**
+ * GitOptionParser
+ */
+function GitOptionParser(method, options) {
+ this.method = method;
+ this.rawOptions = options;
+
+ this.supportedMap = this.getMasterOptionMap()[method];
+ if (this.supportedMap === undefined) {
+ throw new Error('No option map for ' + method);
+ }
+
+ this.generalArgs = [];
+ this.explodeAndSet();
+}
+
+GitOptionParser.prototype.getMasterOptionMap = function() {
+ // here a value of false means that we support it, even if its just a
+ // pass-through option. If the value is not here (aka will be undefined
+ // when accessed), we do not support it.
+ return {
+ commit: {
+ '--amend': false,
+ '-a': false, // warning
+ '-am': false, // warning
+ '-m': false
+ },
+ status: {},
+ log: {},
+ add: {},
+ 'cherry-pick': {},
+ branch: {
+ '-d': false,
+ '-D': false,
+ '-f': false,
+ '--contains': false
+ },
+ checkout: {
+ '-b': false,
+ '-B': false,
+ '-': false
+ },
+ reset: {
+ '--hard': false,
+ '--soft': false // this will raise an error but we catch it in gitEngine
+ },
+ merge: {},
+ rebase: {
+ '-i': false // the mother of all options
+ },
+ revert: {},
+ show: {}
+ };
+};
+
+GitOptionParser.prototype.explodeAndSet = function() {
+ // split on spaces, except when inside quotes
+
+ var exploded = this.rawOptions.match(/('.*?'|".*?"|\S+)/g) || [];
+
+ for (var i = 0; i < exploded.length; i++) {
+ var part = exploded[i];
+ if (part.slice(0,1) == '-') {
+ // it's an option, check supportedMap
+ if (this.supportedMap[part] === undefined) {
+ throw new CommandProcessError({
+ msg: 'The option "' + part + '" is not supported'
+ });
+ }
+
+ // go through and include all the next args until we hit another option or the end
+ var optionArgs = [];
+ var next = i + 1;
+ while (next < exploded.length && exploded[next].slice(0,1) != '-') {
+ optionArgs.push(exploded[next]);
+ next += 1;
+ }
+ i = next - 1;
+
+ // **phew** we are done grabbing those. theseArgs is truthy even with an empty array
+ this.supportedMap[part] = optionArgs;
+ } else {
+ // must be a general arg
+ this.generalArgs.push(part);
+ }
+ }
+};
+
+exports.shortcutMap = shortcutMap;
+exports.instantCommands = instantCommands;
+exports.parse = parse;
+exports.regexMap = regexMap;
+
+});
+
+require.define("/src/js/level/parseWaterfall.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+
+var GitCommands = require('../git/commands');
+var SandboxCommands = require('../level/SandboxCommands');
+
+// more or less a static class
+function ParseWaterfall(options) {
+ this.shortcutWaterfall = [
+ GitCommands.shortcutMap
+ ];
+
+ this.instantWaterfall = [
+ GitCommands.instantCommands,
+ SandboxCommands.instantCommands
+ ];
+
+ this.parseWaterfall = [
+ GitCommands.parse
+ ];
+}
+
+ParseWaterfall.prototype.expandAllShortcuts = function(commandStr) {
+ _.each(this.shortcutWaterfall, function(shortcutMap) {
+ commandStr = this.expandShortcut(commandStr, shortcutMap);
+ }, this);
+ return commandStr;
+};
+
+ParseWaterfall.prototype.expandShortcut = function(commandStr, shortcutMap) {
+ _.each(shortcutMap, function(regex, method) {
+ var results = regex.exec(commandStr);
+ if (results) {
+ commandStr = method + ' ' + commandStr.slice(results[0].length);
+ }
+ });
+ return commandStr;
+};
+
+ParseWaterfall.prototype.processAllInstants = function(commandStr) {
+ _.each(this.instantWaterfall, function(instantCommands) {
+ this.processInstant(commandStr, instantCommands);
+ }, this);
+};
+
+ParseWaterfall.prototype.processInstant = function(commandStr, instantCommands) {
+ _.each(instantCommands, function(tuple) {
+ var regex = tuple[0];
+ var results = regex.exec(commandStr);
+ if (results) {
+ // this will throw a result because it's an instant
+ tuple[1](results);
+ }
+ });
+};
+
+ParseWaterfall.prototype.parseAll = function(commandStr) {
+ var toReturn = false;
+ _.each(this.parseWaterfall, function(parseFunc) {
+ var results = parseFunc(commandStr);
+ if (results) {
+ toReturn = results;
+ }
+ }, this);
+
+ return toReturn;
+};
+
+exports.ParseWaterfall = ParseWaterfall;
+
+
+});
+
+require.define("/src/js/level/SandboxCommands.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+
+var GitCommands = require('../git/commands');
+var GitOptionParser = GitCommands.GitOptionParser;
+
+var Errors = require('../util/errors');
+var CommandProcessError = Errors.CommandProcessError;
+var GitError = Errors.GitError;
+var Warning = Errors.Warning;
+var CommandResult = Errors.CommandResult;
+
+var instantCommands = [
+ [/^ls/, function() {
+ throw new CommandResult({
+ msg: "DontWorryAboutFilesInThisDemo.txt"
+ });
+ }],
+ [/^cd/, function() {
+ throw new CommandResult({
+ msg: "Directory Changed to '/directories/dont/matter/in/this/demo'"
+ });
+ }],
+ [/^refresh$/, function() {
+ var events = require('../app').getEvents();
+
+ events.trigger('refreshTree');
+ throw new CommandResult({
+ msg: "Refreshing tree..."
+ });
+ }],
+ [/^rollup (\d+)$/, function(bits) {
+ var events = require('../app').getEvents();
+
+ // go roll up these commands by joining them with semicolons
+ events.trigger('rollupCommands', bits[1]);
+ throw new CommandResult({
+ msg: 'Commands combined!'
+ });
+ }]
+];
+
+exports.instantCommands = instantCommands;
+
+});
+
+require.define("/src/js/util/keyboard.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
+var Backbone = require('backbone');
+
+var mapKeycodeToKey = function(keycode) {
+ // TODO -- internationalize? Dvorak? I have no idea
+ var keyMap = {
+ 37: 'left',
+ 38: 'up',
+ 39: 'right',
+ 40: 'down',
+ 27: 'esc',
+ 13: 'enter'
+ };
+ return keyMap[keycode];
+};
+
+function KeyboardListener(options) {
+ this.events = options.events || _.clone(Backbone.Events);
+ this.aliasMap = options.aliasMap || {};
+
+ this.keydownListener = _.bind(this.keydown, this);
+ this.listen();
+}
+
+KeyboardListener.prototype.listen = function() {
+ $(document).bind('keydown', this.keydownListener);
+};
+
+KeyboardListener.prototype.mute = function() {
+ $(document).unbind('keydown', this.keydownListener);
+};
+
+KeyboardListener.prototype.keydown = function(e) {
+ var which = e.which;
+
+ var key = mapKeycodeToKey(which);
+ if (key === undefined) {
+ return;
+ }
+
+ this.fireEvent(key);
+};
+
+KeyboardListener.prototype.fireEvent = function(eventName) {
+ eventName = this.aliasMap[eventName] || eventName;
+ this.events.trigger(eventName);
+};
+
+exports.KeyboardListener = KeyboardListener;
+exports.mapKeycodeToKey = mapKeycodeToKey;
+
+
});
require.define("/node_modules/markdown/package.json",function(require,module,exports,__dirname,__filename,process,global){module.exports = {"main":"./lib/index.js"}
@@ -10986,1077 +12079,6 @@ EventEmitter.prototype.listeners = function(type) {
return this._events[type];
};
-});
-
-require.define("/src/js/models/commandModel.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-// horrible hack to get localStorage Backbone plugin
-var Backbone = (!require('../util').isBrowser()) ? Backbone = require('backbone') : Backbone = window.Backbone;
-
-var Errors = require('../util/errors');
-var GitCommands = require('../git/commands');
-var GitOptionParser = GitCommands.GitOptionParser;
-
-var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall;
-
-var CommandProcessError = Errors.CommandProcessError;
-var GitError = Errors.GitError;
-var Warning = Errors.Warning;
-var CommandResult = Errors.CommandResult;
-
-var Command = Backbone.Model.extend({
- defaults: {
- status: 'inqueue',
- rawStr: null,
- result: '',
- createTime: null,
-
- error: null,
- warnings: null,
- parseWaterfall: new ParseWaterfall(),
-
- generalArgs: null,
- supportedMap: null,
- options: null,
- method: null
-
- },
-
- initialize: function(options) {
- this.initDefaults();
- this.validateAtInit();
-
- this.on('change:error', this.errorChanged, this);
- // catch errors on init
- if (this.get('error')) {
- this.errorChanged();
- }
-
- this.parseOrCatch();
- },
-
- initDefaults: function() {
- // weird things happen with defaults if you dont
- // make new objects
- this.set('generalArgs', []);
- this.set('supportedMap', {});
- this.set('warnings', []);
- },
-
- validateAtInit: function() {
- if (this.get('rawStr') === null) {
- throw new Error('Give me a string!');
- }
- if (!this.get('createTime')) {
- this.set('createTime', new Date().toString());
- }
- },
-
- setResult: function(msg) {
- this.set('result', msg);
- },
-
- addWarning: function(msg) {
- this.get('warnings').push(msg);
- // change numWarnings so the change event fires. This is bizarre -- Backbone can't
- // detect if an array changes, so adding an element does nothing
- this.set('numWarnings', this.get('numWarnings') ? this.get('numWarnings') + 1 : 1);
- },
-
- getFormattedWarnings: function() {
- if (!this.get('warnings').length) {
- return '';
- }
- var i = '';
- return '' + i + this.get('warnings').join('
' + i) + '
';
- },
-
- parseOrCatch: function() {
- this.expandShortcuts(this.get('rawStr'));
- try {
- this.processInstants();
- } catch (err) {
- Errors.filterError(err);
- // errorChanged() will handle status and all of that
- this.set('error', err);
- return;
- }
-
- if (this.parseAll()) {
- // something in our parse waterfall succeeded
- return;
- }
-
- // if we reach here, this command is not supported :-/
- this.set('error', new CommandProcessError({
- msg: 'The command "' + this.get('rawStr') + '" isn\'t supported, sorry!'
- })
- );
- },
-
- errorChanged: function() {
- var err = this.get('error');
- if (err instanceof CommandProcessError ||
- err instanceof GitError) {
- this.set('status', 'error');
- } else if (err instanceof CommandResult) {
- this.set('status', 'finished');
- } else if (err instanceof Warning) {
- this.set('status', 'warning');
- }
- this.formatError();
- },
-
- formatError: function() {
- this.set('result', this.get('error').toResult());
- },
-
- expandShortcuts: function(str) {
- str = this.get('parseWaterfall').expandAllShortcuts(str);
- this.set('rawStr', str);
- },
-
- processInstants: function() {
- var str = this.get('rawStr');
- // first if the string is empty, they just want a blank line
- if (!str.length) {
- throw new CommandResult({msg: ""});
- }
-
- // then instant commands that will throw
- this.get('parseWaterfall').processAllInstants(str);
- },
-
- parseAll: function() {
- var str = this.get('rawStr');
- var results = this.get('parseWaterfall').parseAll(str);
-
- if (!results) {
- // nothing parsed successfully
- return false;
- }
-
- _.each(results.toSet, function(obj, key) {
- this.set(key, obj);
- }, this);
- return true;
- }
-});
-
-// command entry is for the commandview
-var CommandEntry = Backbone.Model.extend({
- defaults: {
- text: ''
- },
- // stub out if no plugin available
- localStorage: (Backbone.LocalStorage) ? new Backbone.LocalStorage('CommandEntries') : null
-});
-
-exports.CommandEntry = CommandEntry;
-exports.Command = Command;
-
-});
-
-require.define("/src/js/git/commands.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-
-var Errors = require('../util/errors');
-var CommandProcessError = Errors.CommandProcessError;
-var GitError = Errors.GitError;
-var Warning = Errors.Warning;
-var CommandResult = Errors.CommandResult;
-
-var shortcutMap = {
- 'git commit': /^gc($|\s)/,
- 'git add': /^ga($|\s)/,
- 'git checkout': /^go($|\s)/,
- 'git rebase': /^gr($|\s)/,
- 'git branch': /^gb($|\s)/,
- 'git status': /^gs($|\s)/,
- 'git help': /^git$/
-};
-
-var instantCommands = [
- [/^git help($|\s)/, function() {
- var lines = [
- 'Git Version PCOTTLE.1.0',
- '
',
- 'Usage:',
- _.escape('\t git []'),
- '
',
- 'Supported commands:',
- '
'
- ];
- var commands = GitOptionParser.prototype.getMasterOptionMap();
-
- // build up a nice display of what we support
- _.each(commands, function(commandOptions, command) {
- lines.push('git ' + command);
- _.each(commandOptions, function(vals, optionName) {
- lines.push('\t ' + optionName);
- }, this);
- }, this);
-
- // format and throw
- var msg = lines.join('\n');
- msg = msg.replace(/\t/g, ' ');
- throw new CommandResult({
- msg: msg
- });
- }]
-];
-
-var regexMap = {
- // ($|\s) means that we either have to end the string
- // after the command or there needs to be a space for options
- commit: /^commit($|\s)/,
- add: /^add($|\s)/,
- checkout: /^checkout($|\s)/,
- rebase: /^rebase($|\s)/,
- reset: /^reset($|\s)/,
- branch: /^branch($|\s)/,
- revert: /^revert($|\s)/,
- log: /^log($|\s)/,
- merge: /^merge($|\s)/,
- show: /^show($|\s)/,
- status: /^status($|\s)/,
- 'cherry-pick': /^cherry-pick($|\s)/
-};
-
-var parse = function(str) {
- // now slice off command part
- var fullCommand = str.slice('git '.length);
- var method;
- var options;
-
- // see if we support this particular command
- _.each(regexMap, function(regex, thisMethod) {
- if (regex.exec(fullCommand)) {
- options = fullCommand.slice(thisMethod.length + 1);
- method = thisMethod;
- }
- }, this);
-
- if (!method) {
- return false;
- }
-
- // we support this command!
- // parse off the options and assemble the map / general args
- var parsedOptions = new GitOptionParser(method, options);
- return {
- toSet: {
- generalArgs: parsedOptions.generalArgs,
- supportedMap: parsedOptions.supportedMap,
- method: method,
- options: options
- }
- };
-};
-
-/**
- * GitOptionParser
- */
-function GitOptionParser(method, options) {
- this.method = method;
- this.rawOptions = options;
-
- this.supportedMap = this.getMasterOptionMap()[method];
- if (this.supportedMap === undefined) {
- throw new Error('No option map for ' + method);
- }
-
- this.generalArgs = [];
- this.explodeAndSet();
-}
-
-GitOptionParser.prototype.getMasterOptionMap = function() {
- // here a value of false means that we support it, even if its just a
- // pass-through option. If the value is not here (aka will be undefined
- // when accessed), we do not support it.
- return {
- commit: {
- '--amend': false,
- '-a': false, // warning
- '-am': false, // warning
- '-m': false
- },
- status: {},
- log: {},
- add: {},
- 'cherry-pick': {},
- branch: {
- '-d': false,
- '-D': false,
- '-f': false,
- '--contains': false
- },
- checkout: {
- '-b': false,
- '-B': false,
- '-': false
- },
- reset: {
- '--hard': false,
- '--soft': false // this will raise an error but we catch it in gitEngine
- },
- merge: {},
- rebase: {
- '-i': false // the mother of all options
- },
- revert: {},
- show: {}
- };
-};
-
-GitOptionParser.prototype.explodeAndSet = function() {
- // split on spaces, except when inside quotes
-
- var exploded = this.rawOptions.match(/('.*?'|".*?"|\S+)/g) || [];
-
- for (var i = 0; i < exploded.length; i++) {
- var part = exploded[i];
- if (part.slice(0,1) == '-') {
- // it's an option, check supportedMap
- if (this.supportedMap[part] === undefined) {
- throw new CommandProcessError({
- msg: 'The option "' + part + '" is not supported'
- });
- }
-
- // go through and include all the next args until we hit another option or the end
- var optionArgs = [];
- var next = i + 1;
- while (next < exploded.length && exploded[next].slice(0,1) != '-') {
- optionArgs.push(exploded[next]);
- next += 1;
- }
- i = next - 1;
-
- // **phew** we are done grabbing those. theseArgs is truthy even with an empty array
- this.supportedMap[part] = optionArgs;
- } else {
- // must be a general arg
- this.generalArgs.push(part);
- }
- }
-};
-
-exports.shortcutMap = shortcutMap;
-exports.instantCommands = instantCommands;
-exports.parse = parse;
-exports.regexMap = regexMap;
-
-});
-
-require.define("/src/js/level/parseWaterfall.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-
-var GitCommands = require('../git/commands');
-var SandboxCommands = require('../level/SandboxCommands');
-
-// more or less a static class
-function ParseWaterfall(options) {
- this.shortcutWaterfall = [
- GitCommands.shortcutMap
- ];
-
- this.instantWaterfall = [
- GitCommands.instantCommands,
- SandboxCommands.instantCommands
- ];
-
- this.parseWaterfall = [
- GitCommands.parse
- ];
-}
-
-ParseWaterfall.prototype.expandAllShortcuts = function(commandStr) {
- _.each(this.shortcutWaterfall, function(shortcutMap) {
- commandStr = this.expandShortcut(commandStr, shortcutMap);
- }, this);
- return commandStr;
-};
-
-ParseWaterfall.prototype.expandShortcut = function(commandStr, shortcutMap) {
- _.each(shortcutMap, function(regex, method) {
- var results = regex.exec(commandStr);
- if (results) {
- commandStr = method + ' ' + commandStr.slice(results[0].length);
- }
- });
- return commandStr;
-};
-
-ParseWaterfall.prototype.processAllInstants = function(commandStr) {
- _.each(this.instantWaterfall, function(instantCommands) {
- this.processInstant(commandStr, instantCommands);
- }, this);
-};
-
-ParseWaterfall.prototype.processInstant = function(commandStr, instantCommands) {
- _.each(instantCommands, function(tuple) {
- var regex = tuple[0];
- var results = regex.exec(commandStr);
- if (results) {
- // this will throw a result because it's an instant
- tuple[1](results);
- }
- });
-};
-
-ParseWaterfall.prototype.parseAll = function(commandStr) {
- var toReturn = false;
- _.each(this.parseWaterfall, function(parseFunc) {
- var results = parseFunc(commandStr);
- if (results) {
- toReturn = results;
- }
- }, this);
-
- return toReturn;
-};
-
-exports.ParseWaterfall = ParseWaterfall;
-
-
-});
-
-require.define("/src/js/level/SandboxCommands.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-
-var GitCommands = require('../git/commands');
-var GitOptionParser = GitCommands.GitOptionParser;
-
-var Errors = require('../util/errors');
-var CommandProcessError = Errors.CommandProcessError;
-var GitError = Errors.GitError;
-var Warning = Errors.Warning;
-var CommandResult = Errors.CommandResult;
-
-var instantCommands = [
- [/^ls/, function() {
- throw new CommandResult({
- msg: "DontWorryAboutFilesInThisDemo.txt"
- });
- }],
- [/^cd/, function() {
- throw new CommandResult({
- msg: "Directory Changed to '/directories/dont/matter/in/this/demo'"
- });
- }],
- [/^refresh$/, function() {
- var events = require('../app').getEvents();
-
- events.trigger('refreshTree');
- throw new CommandResult({
- msg: "Refreshing tree..."
- });
- }],
- [/^rollup (\d+)$/, function(bits) {
- var events = require('../app').getEvents();
-
- // go roll up these commands by joining them with semicolons
- events.trigger('rollupCommands', bits[1]);
- throw new CommandResult({
- msg: 'Commands combined!'
- });
- }]
-];
-
-exports.instantCommands = instantCommands;
-
-});
-
-require.define("/src/js/app/index.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-var Backbone = require('backbone');
-
-/**
- * Globals
- */
-var events = _.clone(Backbone.Events);
-var ui;
-var mainVis;
-var eventBaton;
-
-///////////////////////////////////////////////////////////////////////
-
-var init = function(){
- var Visualization = require('../visuals/visualization').Visualization;
- var EventBaton = require('../util/eventBaton').EventBaton;
-
- eventBaton = new EventBaton();
- ui = new UI();
- mainVis = new Visualization({
- el: $('#canvasWrapper')[0]
- });
-
- // we always want to focus the text area to collect input
- var focusTextArea = function() {
- console.log('focusing text area');
- $('#commandTextField').focus();
- };
- focusTextArea();
-
- $(window).focus(focusTextArea);
- $(document).click(focusTextArea);
-
- // but when the input is fired in the text area, we pipe that to whoever is
- // listenining
- var makeKeyListener = function(name) {
- return function() {
- var args = [name];
- _.each(arguments, function(arg) {
- args.push(arg);
- });
- eventBaton.trigger.apply(eventBaton, args);
- };
- };
-
- $('#commandTextField').on('keydown', makeKeyListener('keydown'));
- $('#commandTextField').on('keyup', makeKeyListener('keyup'));
-
- /* hacky demo functionality */
- if (/\?demo/.test(window.location.href)) {
- setTimeout(function() {
- events.trigger('submitCommandValueFromEvent', "gc; git checkout HEAD~1; git commit; git checkout -b bugFix; gc; gc; git rebase -i HEAD~2; git rebase master; git checkout master; gc; gc; git merge bugFix");
- }, 500);
- }
-};
-
-$(document).ready(init);
-
-function UI() {
- this.active = true;
- var Collections = require('../models/collections');
- var CommandViews = require('../views/commandViews');
-
- this.commandCollection = new Collections.CommandCollection();
- this.commandBuffer = new Collections.CommandBuffer({
- collection: this.commandCollection
- });
-
- this.commandPromptView = new CommandViews.CommandPromptView({
- el: $('#commandLineBar'),
- collection: this.commandCollection
- });
- this.commandLineHistoryView = new CommandViews.CommandLineHistoryView({
- el: $('#commandLineHistory'),
- collection: this.commandCollection
- });
-
- $('#commandTextField').focus();
- eventBaton.stealBaton('windowFocus', this.onWindowFocus, this);
-}
-
-UI.prototype.onWindowFocus = function() {
- this.commandPromptView.focus();
-};
-
-exports.getEvents = function() {
- return events;
-};
-
-exports.getUI = function() {
- return ui;
-};
-
-exports.getMainVis = function() {
- return mainVis;
-};
-
-exports.getEventBaton = function() {
- return eventBaton;
-};
-
-exports.init = init;
-
-
-});
-
-require.define("/src/js/util/eventBaton.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-
-function EventBaton() {
- this.eventMap = {};
-}
-
-// this method steals the "baton" -- aka, only this method will now
-// get called. analogous to events.on
-// EventBaton.prototype.on = function(name, func, context) {
-EventBaton.prototype.stealBaton = function(name, func, context) {
- if (!name) { throw new Error('need name'); }
-
- var listeners = this.eventMap[name] || [];
- listeners.push({
- func: func,
- context: context
- });
- this.eventMap[name] = listeners;
-};
-
-EventBaton.prototype.trigger = function(name) {
- // arguments is weird and doesnt do slice right
- var argsToApply = [];
- for (var i = 1; i < arguments.length; i++) {
- argsToApply.push(arguments[i]);
- }
-
- var listeners = this.eventMap[name];
- if (!listeners) {
- console.warn('no listeners for', name);
- return;
- }
- // call the top most listener with context and such
- var toCall = listeners.slice(-1)[0];
- toCall.func.apply(toCall.context, argsToApply);
-};
-
-EventBaton.prototype.releaseBaton = function(name, func, context) {
- if (!name) { throw new Error('need name'); }
- // might be in the middle of the stack
- var listeners = this.eventMap[name];
- if (!listeners || !listeners.length) {
- throw new Error('no one has that baton!' + name);
- }
-
- var newListeners = [];
- var found = false;
- _.each(listeners, function(listenerObj) {
- if (listenerObj.func === func) {
- found = true;
- } else {
- newListeners.push(listenerObj);
- }
- }, this);
-
- if (!found) {
- throw new Error('did not find that function');
- }
- this.eventMap[name] = newListeners;
-};
-
-exports.EventBaton = EventBaton;
-
-
-});
-
-require.define("/src/js/views/commandViews.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-// horrible hack to get localStorage Backbone plugin
-var Backbone = (!require('../util').isBrowser()) ? Backbone = require('backbone') : Backbone = window.Backbone;
-
-var CommandEntryCollection = require('../models/collections').CommandEntryCollection;
-var Main = require('../app');
-var Command = require('../models/commandModel').Command;
-var CommandEntry = require('../models/commandModel').CommandEntry;
-
-var Errors = require('../util/errors');
-var Warning = Errors.Warning;
-
-var util = require('../util');
-var keyboard = require('../util/keyboard');
-
-var CommandPromptView = Backbone.View.extend({
- initialize: function(options) {
- this.collection = options.collection;
-
- // uses local storage
- this.commands = new CommandEntryCollection();
- this.commands.fetch({
- success: _.bind(function() {
- // reverse the commands. this is ugly but needs to be done...
- var commands = [];
- this.commands.each(function(c) {
- commands.push(c);
- });
-
- commands.reverse();
- this.commands.reset();
-
- _.each(commands, function(c) {
- this.commands.add(c);
- }, this);
- }, this)
- });
-
- this.index = -1;
- this.commandSpan = this.$('#prompt span.command')[0];
- this.commandCursor = this.$('#prompt span.cursor')[0];
- this.focus();
-
- Main.getEvents().on('processCommandFromEvent', this.addToCollection, this);
- Main.getEvents().on('submitCommandValueFromEvent', this.submitValue, this);
- Main.getEvents().on('rollupCommands', this.rollupCommands, this);
-
- Main.getEventBaton().stealBaton('keydown', this.onKeyDown, this);
- Main.getEventBaton().stealBaton('keyup', this.onKeyUp, this);
- },
-
- events: {
- 'blur #commandTextField': 'hideCursor',
- 'focus #commandTextField': 'showCursor'
- },
-
- blur: function() {
- this.hideCursor();
- },
-
- focus: function() {
- this.$('#commandTextField').focus();
- this.showCursor();
- },
-
- hideCursor: function() {
- this.toggleCursor(false);
- },
-
- showCursor: function() {
- this.toggleCursor(true);
- },
-
- toggleCursor: function(state) {
- $(this.commandCursor).toggleClass('shown', state);
- },
-
- onKeyDown: function(e) {
- var el = e.srcElement;
- this.updatePrompt(el);
- },
-
- onKeyUp: function(e) {
- this.onKeyDown(e);
-
- // we need to capture some of these events.
- var keyToFuncMap = {
- enter: _.bind(function() {
- this.submit();
- }, this),
- up: _.bind(function() {
- this.commandSelectChange(1);
- }, this),
- down: _.bind(function() {
- this.commandSelectChange(-1);
- }, this)
- };
-
- var key = keyboard.mapKeycodeToKey(e.which);
- if (keyToFuncMap[key] !== undefined) {
- e.preventDefault();
- keyToFuncMap[key]();
- this.onKeyDown(e);
- }
- },
-
- badHtmlEncode: function(text) {
- return text.replace(/&/g,'&')
- .replace(/= this.commands.length || this.index < 0) {
- this.clear();
- this.index = -1;
- return;
- }
-
- // yay! we actually can display something
- var commandEntry = this.commands.toArray()[this.index].get('text');
- this.setTextField(commandEntry);
- },
-
- clearLocalStorage: function() {
- this.commands.each(function(c) {
- Backbone.sync('delete', c, function() { });
- }, this);
- localStorage.setItem('CommandEntries', '');
- },
-
- setTextField: function(value) {
- this.$('#commandTextField').val(value);
- },
-
- clear: function() {
- this.setTextField('');
- },
-
- submit: function() {
- var value = this.$('#commandTextField').val().replace('\n', '');
- this.clear();
- this.submitValue(value);
- },
-
- rollupCommands: function(numBack) {
- var which = this.commands.toArray().slice(1, Number(numBack) + 1);
- which.reverse();
-
- var str = '';
- _.each(which, function(commandEntry) {
- str += commandEntry.get('text') + ';';
- }, this);
-
- console.log('the str', str);
-
- var rolled = new CommandEntry({text: str});
- this.commands.unshift(rolled);
- Backbone.sync('create', rolled, function() { });
- },
-
- submitValue: function(value) {
- // we should add the command to our local storage history
- // if it's not a blank line and this is a new command...
- // or if we edited the command in place
- var shouldAdd = (value.length && this.index == -1) ||
- ((value.length && this.index !== -1 &&
- this.commands.toArray()[this.index].get('text') !== value));
-
- if (shouldAdd) {
- var commandEntry = new CommandEntry({text: value});
- this.commands.unshift(commandEntry);
-
- // store to local storage
- Backbone.sync('create', commandEntry, function() { });
-
- // if our length is too egregious, reset
- if (this.commands.length > 100) {
- this.clearLocalStorage();
- }
- }
- this.index = -1;
-
- util.splitTextCommand(value, function(command) {
- this.addToCollection(command);
- }, this);
- },
-
- addToCollection: function(value) {
- var command = new Command({
- rawStr: value
- });
- this.collection.add(command);
- }
-});
-
-// This is the view for all commands -- it will represent
-// their status (inqueue, processing, finished, error),
-// their value ("git commit --amend"),
-// and the result (either errors or warnings or whatever)
-var CommandView = Backbone.View.extend({
- tagName: 'div',
- model: Command,
- template: _.template($('#command-template').html()),
-
- events: {
- 'click': 'clicked'
- },
-
- clicked: function(e) {
- },
-
- initialize: function() {
- this.model.bind('change', this.wasChanged, this);
- this.model.bind('destroy', this.remove, this);
- },
-
- wasChanged: function(model, changeEvent) {
- // for changes that are just comestic, we actually only want to toggle classes
- // with jquery rather than brutally delete a html. doing so allows us
- // to nicely fade things
- var changes = changeEvent.changes;
- var changeKeys = _.keys(changes);
- if (_.difference(changeKeys, ['status']).length === 0) {
- this.updateStatus();
- } else {
- this.render();
- }
- },
-
- updateStatus: function() {
- var statuses = ['inqueue', 'processing', 'finished'];
- var toggleMap = {};
- _.each(statuses, function(status) {
- toggleMap[status] = false;
- });
- toggleMap[this.model.get('status')] = true;
-
- var query = this.$('p.commandLine');
-
- _.each(toggleMap, function(value, key) {
- query.toggleClass(key, value);
- });
- },
-
- render: function() {
- var json = _.extend(
- {
- resultType: '',
- result: '',
- formattedWarnings: this.model.getFormattedWarnings()
- },
- this.model.toJSON()
- );
-
- this.$el.html(this.template(json));
- return this;
- },
-
- remove: function() {
- $(this.el).hide();
- }
-});
-
-
-var CommandLineHistoryView = Backbone.View.extend({
- initialize: function(options) {
- this.collection = options.collection;
-
- this.collection.on('add', this.addOne, this);
- this.collection.on('reset', this.addAll, this);
- this.collection.on('all', this.render, this);
-
- this.collection.on('change', this.scrollDown, this);
-
- Main.getEvents().on('issueWarning', this.addWarning, this);
- Main.getEvents().on('commandScrollDown', this.scrollDown, this);
- },
-
- addWarning: function(msg) {
- var err = new Warning({
- msg: msg
- });
-
- var command = new Command({
- error: err,
- rawStr: 'Warning:'
- });
-
- this.collection.add(command);
- },
-
- scrollDown: function() {
- // if commandDisplay is ever bigger than #terminal, we need to
- // add overflow-y to terminal and scroll down
- var cD = $('#commandDisplay')[0];
- var t = $('#terminal')[0];
-
- if ($(t).hasClass('scrolling')) {
- t.scrollTop = t.scrollHeight;
- return;
- }
- if (cD.clientHeight > t.clientHeight) {
- $(t).css('overflow-y', 'scroll');
- $(t).css('overflow-x', 'hidden');
- $(t).addClass('scrolling');
- t.scrollTop = t.scrollHeight;
- }
- },
-
- addOne: function(command) {
- var view = new CommandView({
- model: command
- });
- this.$('#commandDisplay').append(view.render().el);
- this.scrollDown();
- },
-
- addAll: function() {
- this.collection.each(this.addOne);
- }
-});
-
-exports.CommandPromptView = CommandPromptView;
-exports.CommandLineHistoryView = CommandLineHistoryView;
-
-});
-
-require.define("/src/js/util/keyboard.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
-var Backbone = require('backbone');
-
-var mapKeycodeToKey = function(keycode) {
- // TODO -- internationalize? Dvorak? I have no idea
- var keyMap = {
- 37: 'left',
- 38: 'up',
- 39: 'right',
- 40: 'down',
- 27: 'esc',
- 13: 'enter'
- };
- return keyMap[keycode];
-};
-
-function KeyboardListener(options) {
- this.events = options.events || _.clone(Backbone.Events);
- this.aliasMap = options.aliasMap || {};
-
- this.keydownListener = _.bind(this.keydown, this);
- this.listen();
-}
-
-KeyboardListener.prototype.listen = function() {
- $(document).bind('keydown', this.keydownListener);
-};
-
-KeyboardListener.prototype.mute = function() {
- $(document).unbind('keydown', this.keydownListener);
-};
-
-KeyboardListener.prototype.keydown = function(e) {
- var which = e.which;
-
- var key = mapKeycodeToKey(which);
- if (key === undefined) {
- return;
- }
-
- this.fireEvent(key);
-};
-
-KeyboardListener.prototype.fireEvent = function(eventName) {
- eventName = this.aliasMap[eventName] || eventName;
- this.events.trigger(eventName);
-};
-
-exports.KeyboardListener = KeyboardListener;
-exports.mapKeycodeToKey = mapKeycodeToKey;
-
-
});
require.define("/src/js/visuals/index.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore');
@@ -17571,6 +17593,7 @@ require.define("/src/js/views/index.js",function(require,module,exports,__dirnam
var _ = require('underscore');
// horrible hack to get localStorage Backbone plugin
var Backbone = (!require('../util').isBrowser()) ? require('backbone') : window.Backbone;
+var Main = require('../app');
var BaseView = Backbone.View.extend({
getDestination: function() {
@@ -17681,6 +17704,7 @@ var ModalView = Backbone.View.extend({
initialize: function(options) {
this.render();
+ this.stealKeyboard();
},
render: function() {
@@ -17689,6 +17713,25 @@ var ModalView = Backbone.View.extend({
$('body').append(this.el);
},
+ stealKeyboard: function() {
+ console.warn('stealing keyboard');
+ Main.getEventBaton().stealBaton('keydown', this.onKeyDown, this);
+ Main.getEventBaton().stealBaton('keyup', this.onKeyUp, this);
+ },
+
+ releaseKeyboard: function() {
+ Main.getEventBaton().releaseBaton('keydown', this.onKeyDown, this);
+ Main.getEventBaton().releaseBaton('keyup', this.onKeyUp, this);
+ },
+
+ onKeyDown: function(e) {
+ e.preventDefault();
+ },
+
+ onKeyUp: function(e) {
+ e.preventDefault();
+ },
+
show: function() {
this.toggleZ(true);
this.toggleShow(true);
@@ -17719,6 +17762,7 @@ var ModalView = Backbone.View.extend({
tearDown: function() {
this.$el.html('');
$('body')[0].removeChild(this.el);
+ this.releaseKeyboard();
}
});
diff --git a/src/js/views/index.js b/src/js/views/index.js
index 8e5590e2..5ea4ca9a 100644
--- a/src/js/views/index.js
+++ b/src/js/views/index.js
@@ -2,6 +2,7 @@ var GitError = require('../util/errors').GitError;
var _ = require('underscore');
// horrible hack to get localStorage Backbone plugin
var Backbone = (!require('../util').isBrowser()) ? require('backbone') : window.Backbone;
+var Main = require('../app');
var BaseView = Backbone.View.extend({
getDestination: function() {
@@ -112,6 +113,7 @@ var ModalView = Backbone.View.extend({
initialize: function(options) {
this.render();
+ this.stealKeyboard();
},
render: function() {
@@ -120,6 +122,25 @@ var ModalView = Backbone.View.extend({
$('body').append(this.el);
},
+ stealKeyboard: function() {
+ console.warn('stealing keyboard');
+ Main.getEventBaton().stealBaton('keydown', this.onKeyDown, this);
+ Main.getEventBaton().stealBaton('keyup', this.onKeyUp, this);
+ },
+
+ releaseKeyboard: function() {
+ Main.getEventBaton().releaseBaton('keydown', this.onKeyDown, this);
+ Main.getEventBaton().releaseBaton('keyup', this.onKeyUp, this);
+ },
+
+ onKeyDown: function(e) {
+ e.preventDefault();
+ },
+
+ onKeyUp: function(e) {
+ e.preventDefault();
+ },
+
show: function() {
this.toggleZ(true);
this.toggleShow(true);
@@ -150,6 +171,7 @@ var ModalView = Backbone.View.extend({
tearDown: function() {
this.$el.html('');
$('body')[0].removeChild(this.el);
+ this.releaseKeyboard();
}
});