diff --git a/build/bundle.js b/build/bundle.js index 62b43eb8..49709777 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -4844,16 +4844,13 @@ var Backbone = require('backbone'); var Constants = require('../util/constants'); var Views = require('../views'); var util = require('../util'); -var Command = require('../models/commandModel').Command; -var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall; -var DisabledMap = require('../level/disabledMap').DisabledMap; /** * Globals */ var events = _.clone(Backbone.Events); var commandUI; -var mainVis; +var sandbox; var eventBaton; /////////////////////////////////////////////////////////////////////// @@ -4868,14 +4865,12 @@ var init = function() { * - initializing the command input bar * - handling window.focus and zoom events **/ - var Visualization = require('../visuals/visualization').Visualization; + var Sandbox = require('../level/sandbox').Sandbox; var EventBaton = require('../util/eventBaton').EventBaton; eventBaton = new EventBaton(); commandUI = new CommandUI(); - mainVis = new Visualization({ - el: $('#canvasWrapper')[0] - }); + sandbox = new Sandbox(); // we always want to focus the text area to collect input var focusTextArea = function() { @@ -4953,15 +4948,77 @@ function CommandUI() { el: $('#commandLineHistory'), collection: this.commandCollection }); - - this.parseWaterfall = new ParseWaterfall(); - this.parseWaterfall.addFirst('instantWaterfall', new DisabledMap().getInstantCommands()); - - eventBaton.stealBaton('commandSubmitted', this.commandSubmitted, this); } -CommandUI.prototype.commandSubmitted = function(value) { - events.trigger('commandSubmittedPassive', value); +exports.getEvents = function() { + return events; +}; + +exports.getSandbox = function() { + return sandbox; +}; + +exports.getEventBaton = function() { + return eventBaton; +}; + +exports.getCommandUI = function() { + return commandUI; +} + +exports.init = init; + + +}); + +require.define("/src/js/level/sandbox.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore'); +var Backbone = require('backbone'); + +var util = require('../util'); +var Main = require('../app'); + +var Visualization = require('../visuals/visualization').Visualization; +var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall; +var DisabledMap = require('../level/disabledMap').DisabledMap; +var Command = require('../models/commandModel').Command; + +var ModalTerminal = require('../views').ModalTerminal; +var ContainedBase = require('../views').ContainedBase; +var ConfirmCancelView = require('../views').ConfirmCancelView; +var LeftRightView = require('../views').LeftRightView; +var ModalAlert = require('../views').ModalAlert; + +function Sandbox(options) { + options = options || {}; + + this.mainVis = new Visualization({ + el: options.el || $('#canvasWrapper')[0] + }); + + // don't add it to just any collection -- adding to the + // CommandUI collection will put in history + this.commandCollection = Main.getCommandUI().commandCollection; + + this.parseWaterfall = new ParseWaterfall(); + this.parseWaterfall.addFirst( + 'instantWaterfall', + new DisabledMap().getInstantCommands() + ); + + if (!options.defer) { + this.takeControl(); + } +} + +Sandbox.prototype.takeControl = function() { + Main.getEventBaton().stealBaton('commandSubmitted', this.commandSubmitted, this); + Main.getEvents().on('processSandboxCommand', this.processSandboxCommand, this); +}; + +Sandbox.prototype.commandSubmitted = function(value) { + // allow other things to see this command + Main.getEvents().trigger('commandSubmittedPassive', value); + util.splitTextCommand(value, function(command) { this.commandCollection.add(new Command({ rawStr: command, @@ -4970,568 +5027,24 @@ CommandUI.prototype.commandSubmitted = function(value) { }, this); }; -exports.getEvents = function() { - return events; -}; - -exports.getUI = function() { - return commandUI; -}; - -exports.getMainVis = function() { - return mainVis; -}; - -exports.getEventBaton = function() { - return eventBaton; -}; - -exports.init = init; - - -}); - -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) { - // data comes back from the parsing functions like - // options (etc) that need to be set - 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 - 'git commit': /^git commit($|\s)/, - 'git add': /^git add($|\s)/, - 'git checkout': /^git checkout($|\s)/, - 'git rebase': /^git rebase($|\s)/, - 'git reset': /^git reset($|\s)/, - 'git branch': /^git branch($|\s)/, - 'git revert': /^git revert($|\s)/, - 'git log': /^git log($|\s)/, - 'git merge': /^git merge($|\s)/, - 'git show': /^git show($|\s)/, - 'git status': /^git status($|\s)/, - 'git cherry-pick': /^git cherry-pick($|\s)/ -}; - -var parse = function(str) { - var method; - var options; - - // see if we support this particular command - _.each(regexMap, function(regex, thisMethod) { - if (regex.exec(str)) { - options = str.slice(thisMethod.length + 1); - method = thisMethod.slice('git '.length); - } - }, 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, - eventName: 'processGitCommand' - } +Sandbox.prototype.processSandboxCommand = function(command, callback) { + var commandMap = { + help: this.helpDialog }; + var method = commandMap[command.get('method')]; + if (!method) { throw new Error('no method for that wut'); } + + method.apply(this, [command, callback]); }; -/** - * 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) { - options = options || {}; - this.shortcutWaterfall = options.shortcutWaterfall || [ - GitCommands.shortcutMap - ]; - - this.instantWaterfall = options.instantWaterfall || [ - GitCommands.instantCommands, - SandboxCommands.instantCommands - ]; - - this.parseWaterfall = options.parseWaterfall || [ - GitCommands.parse - ]; -} - -ParseWaterfall.prototype.clone = function() { - return new ParseWaterfall({ - shortcutWaterfall: this.shortcutWaterfall.slice(), - instantWaterfall: this.instantWaterfall.slice(), - parseWaterfall: this.parseWaterfall.slice() +Sandbox.prototype.helpDialog = function(command, callback) { + var a = new ModalAlert({ + markdown: '#wutttt' }); + a.show(); }; -ParseWaterfall.prototype.getWaterfallMap = function() { - return { - shortcutWaterfall: this.shortcutWaterfall, - instantWaterfall: this.instantWaterfall, - parseWaterfall: this.parseWaterfall - }; -}; - -ParseWaterfall.prototype.addFirst = function(which, value) { - this.getWaterfallMap()[which].unshift(value); -}; - -ParseWaterfall.prototype.addLast = function(which, value) { - this.getWaterfallMap()[which].push(value); -}; - -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/level/disabledMap.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore'); - -var GitCommands = require('../git/commands'); - -var Errors = require('../util/errors'); -var GitError = Errors.GitError; - -function DisabledMap(options) { - options = options || {}; - this.disabledMap = options.disabledMap || { - 'git cherry-pick': true, - 'git rebase': true - }; -} - -DisabledMap.prototype.getInstantCommands = function() { - // this produces an array of regex / function pairs that can be - // piped into a parse waterfall to disable certain git commmands - // :D - var instants = []; - var onMatch = function() { - throw new GitError({ - msg: 'That git command is disabled for this level!' - }); - }; - - _.each(this.disabledMap, function(val, disabledCommand) { - var gitRegex = GitCommands.regexMap[disabledCommand]; - if (!gitRegex) { - throw new Error('wuttttt this disbaled command' + disabledCommand + - ' has no regex matching'); - } - instants.push([gitRegex, onMatch]); - }); - return instants; -}; - -exports.DisabledMap = DisabledMap; - +exports.Sandbox = Sandbox; }); @@ -5705,6 +5218,8 @@ var CommandBuffer = Backbone.Model.extend({ }, processCommand: function(command) { + command.set('status', 'processing'); + var callback = _.bind(function() { this.setTimeout(); }, this); @@ -7100,7 +6615,6 @@ GitEngine.prototype.dispatch = function(command, callback) { callback: whenDone }); - command.set('status', 'processing'); try { var methodName = command.get('method').replace(/-/g, '') + 'Starter'; this[methodName](); @@ -9621,6 +9135,532 @@ var RebaseEntryView = Backbone.View.extend({ exports.InteractiveRebaseView = InteractiveRebaseView; +}); + +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) { + // data comes back from the parsing functions like + // options (etc) that need to be set + 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 + 'git commit': /^git commit($|\s)/, + 'git add': /^git add($|\s)/, + 'git checkout': /^git checkout($|\s)/, + 'git rebase': /^git rebase($|\s)/, + 'git reset': /^git reset($|\s)/, + 'git branch': /^git branch($|\s)/, + 'git revert': /^git revert($|\s)/, + 'git log': /^git log($|\s)/, + 'git merge': /^git merge($|\s)/, + 'git show': /^git show($|\s)/, + 'git status': /^git status($|\s)/, + 'git cherry-pick': /^git cherry-pick($|\s)/ +}; + +var parse = function(str) { + var method; + var options; + + // see if we support this particular command + _.each(regexMap, function(regex, thisMethod) { + if (regex.exec(str)) { + options = str.slice(thisMethod.length + 1); + method = thisMethod.slice('git '.length); + } + }); + + 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, + eventName: 'processGitCommand' + } + }; +}; + +/** + * 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) { + options = options || {}; + this.shortcutWaterfall = options.shortcutWaterfall || [ + GitCommands.shortcutMap + ]; + + this.instantWaterfall = options.instantWaterfall || [ + GitCommands.instantCommands, + SandboxCommands.instantCommands + ]; + + this.parseWaterfall = options.parseWaterfall || [ + GitCommands.parse, + SandboxCommands.parse + ]; +} + +ParseWaterfall.prototype.clone = function() { + return new ParseWaterfall({ + shortcutWaterfall: this.shortcutWaterfall.slice(), + instantWaterfall: this.instantWaterfall.slice(), + parseWaterfall: this.parseWaterfall.slice() + }); +}; + +ParseWaterfall.prototype.getWaterfallMap = function() { + return { + shortcutWaterfall: this.shortcutWaterfall, + instantWaterfall: this.instantWaterfall, + parseWaterfall: this.parseWaterfall + }; +}; + +ParseWaterfall.prototype.addFirst = function(which, value) { + this.getWaterfallMap()[which].unshift(value); +}; + +ParseWaterfall.prototype.addLast = function(which, value) { + this.getWaterfallMap()[which].push(value); +}; + +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!' + }); + }] +]; + +var regexMap = { + 'help': /^help($|\s)|\?/ +}; + +var parse = function(str) { + var sandboxMethod; + + _.each(regexMap, function(regex, method) { + if (regex.test(str)) { + sandboxMethod = method; + } + }); + + return (!sandboxMethod) ? false : { + toSet: { + eventName: 'processSandboxCommand', + method: sandboxMethod + } + }; +}; + +exports.instantCommands = instantCommands; +exports.parse = parse; + + }); require.define("/src/js/visuals/index.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore'); @@ -11467,6 +11507,48 @@ var VisEdgeCollection = Backbone.Collection.extend({ exports.VisEdgeCollection = VisEdgeCollection; exports.VisEdge = VisEdge; +}); + +require.define("/src/js/level/disabledMap.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore'); + +var GitCommands = require('../git/commands'); + +var Errors = require('../util/errors'); +var GitError = Errors.GitError; + +function DisabledMap(options) { + options = options || {}; + this.disabledMap = options.disabledMap || { + 'git cherry-pick': true, + 'git rebase': true + }; +} + +DisabledMap.prototype.getInstantCommands = function() { + // this produces an array of regex / function pairs that can be + // piped into a parse waterfall to disable certain git commmands + // :D + var instants = []; + var onMatch = function() { + throw new GitError({ + msg: 'That git command is disabled for this level!' + }); + }; + + _.each(this.disabledMap, function(val, disabledCommand) { + var gitRegex = GitCommands.regexMap[disabledCommand]; + if (!gitRegex) { + throw new Error('wuttttt this disbaled command' + disabledCommand + + ' has no regex matching'); + } + instants.push([gitRegex, onMatch]); + }); + return instants; +}; + +exports.DisabledMap = DisabledMap; + + }); require.define("/src/js/util/eventBaton.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore'); @@ -11567,7 +11649,7 @@ var setupZoomPoll = function(callback, context) { currentZoom = newZoom; callback.apply(context, [newZoom]); } - }, 100); + }, 500); }; exports.setupZoomPoll = setupZoomPoll; @@ -14407,16 +14489,13 @@ var Backbone = require('backbone'); var Constants = require('../util/constants'); var Views = require('../views'); var util = require('../util'); -var Command = require('../models/commandModel').Command; -var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall; -var DisabledMap = require('../level/disabledMap').DisabledMap; /** * Globals */ var events = _.clone(Backbone.Events); var commandUI; -var mainVis; +var sandbox; var eventBaton; /////////////////////////////////////////////////////////////////////// @@ -14431,14 +14510,12 @@ var init = function() { * - initializing the command input bar * - handling window.focus and zoom events **/ - var Visualization = require('../visuals/visualization').Visualization; + var Sandbox = require('../level/sandbox').Sandbox; var EventBaton = require('../util/eventBaton').EventBaton; eventBaton = new EventBaton(); commandUI = new CommandUI(); - mainVis = new Visualization({ - el: $('#canvasWrapper')[0] - }); + sandbox = new Sandbox(); // we always want to focus the text area to collect input var focusTextArea = function() { @@ -14516,39 +14593,24 @@ function CommandUI() { el: $('#commandLineHistory'), collection: this.commandCollection }); - - this.parseWaterfall = new ParseWaterfall(); - this.parseWaterfall.addFirst('instantWaterfall', new DisabledMap().getInstantCommands()); - - eventBaton.stealBaton('commandSubmitted', this.commandSubmitted, this); } -CommandUI.prototype.commandSubmitted = function(value) { - events.trigger('commandSubmittedPassive', value); - util.splitTextCommand(value, function(command) { - this.commandCollection.add(new Command({ - rawStr: command, - parseWaterfall: this.parseWaterfall - })); - }, this); -}; - exports.getEvents = function() { return events; }; -exports.getUI = function() { - return commandUI; -}; - -exports.getMainVis = function() { - return mainVis; +exports.getSandbox = function() { + return sandbox; }; exports.getEventBaton = function() { return eventBaton; }; +exports.getCommandUI = function() { + return commandUI; +} + exports.init = init; @@ -14630,7 +14692,7 @@ var parse = function(str) { options = str.slice(thisMethod.length + 1); method = thisMethod.slice('git '.length); } - }, this); + }); if (!method) { return false; @@ -16161,7 +16223,6 @@ GitEngine.prototype.dispatch = function(command, callback) { callback: whenDone }); - command.set('status', 'processing'); try { var methodName = command.get('method').replace(/-/g, '') + 'Starter'; this[methodName](); @@ -16664,7 +16725,8 @@ function ParseWaterfall(options) { ]; this.parseWaterfall = options.parseWaterfall || [ - GitCommands.parse + GitCommands.parse, + SandboxCommands.parse ]; } @@ -16744,6 +16806,84 @@ exports.ParseWaterfall = ParseWaterfall; }); require("/src/js/level/parseWaterfall.js"); +require.define("/src/js/level/sandbox.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore'); +var Backbone = require('backbone'); + +var util = require('../util'); +var Main = require('../app'); + +var Visualization = require('../visuals/visualization').Visualization; +var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall; +var DisabledMap = require('../level/disabledMap').DisabledMap; +var Command = require('../models/commandModel').Command; + +var ModalTerminal = require('../views').ModalTerminal; +var ContainedBase = require('../views').ContainedBase; +var ConfirmCancelView = require('../views').ConfirmCancelView; +var LeftRightView = require('../views').LeftRightView; +var ModalAlert = require('../views').ModalAlert; + +function Sandbox(options) { + options = options || {}; + + this.mainVis = new Visualization({ + el: options.el || $('#canvasWrapper')[0] + }); + + // don't add it to just any collection -- adding to the + // CommandUI collection will put in history + this.commandCollection = Main.getCommandUI().commandCollection; + + this.parseWaterfall = new ParseWaterfall(); + this.parseWaterfall.addFirst( + 'instantWaterfall', + new DisabledMap().getInstantCommands() + ); + + if (!options.defer) { + this.takeControl(); + } +} + +Sandbox.prototype.takeControl = function() { + Main.getEventBaton().stealBaton('commandSubmitted', this.commandSubmitted, this); + Main.getEvents().on('processSandboxCommand', this.processSandboxCommand, this); +}; + +Sandbox.prototype.commandSubmitted = function(value) { + // allow other things to see this command + Main.getEvents().trigger('commandSubmittedPassive', value); + + util.splitTextCommand(value, function(command) { + this.commandCollection.add(new Command({ + rawStr: command, + parseWaterfall: this.parseWaterfall + })); + }, this); +}; + +Sandbox.prototype.processSandboxCommand = function(command, callback) { + var commandMap = { + help: this.helpDialog + }; + var method = commandMap[command.get('method')]; + if (!method) { throw new Error('no method for that wut'); } + + method.apply(this, [command, callback]); +}; + +Sandbox.prototype.helpDialog = function(command, callback) { + var a = new ModalAlert({ + markdown: '#wutttt' + }); + a.show(); +}; + +exports.Sandbox = Sandbox; + +}); +require("/src/js/level/sandbox.js"); + require.define("/src/js/level/sandboxCommands.js",function(require,module,exports,__dirname,__filename,process,global){var _ = require('underscore'); var GitCommands = require('../git/commands'); @@ -16785,7 +16925,30 @@ var instantCommands = [ }] ]; +var regexMap = { + 'help': /^help($|\s)|\?/ +}; + +var parse = function(str) { + var sandboxMethod; + + _.each(regexMap, function(regex, method) { + if (regex.test(str)) { + sandboxMethod = method; + } + }); + + return (!sandboxMethod) ? false : { + toSet: { + eventName: 'processSandboxCommand', + method: sandboxMethod + } + }; +}; + exports.instantCommands = instantCommands; +exports.parse = parse; + }); require("/src/js/level/sandboxCommands.js"); @@ -16869,6 +17032,8 @@ var CommandBuffer = Backbone.Model.extend({ }, processCommand: function(command) { + command.set('status', 'processing'); + var callback = _.bind(function() { this.setTimeout(); }, this); @@ -17161,8 +17326,8 @@ _.each(toGlobalize, function(module) { $(document).ready(function() { window.events = toGlobalize.Main.getEvents(); - window.mainVis = toGlobalize.Main.getMainVis(); - window.ui = toGlobalize.Main.getMainVis(); + window.eventBaton = toGlobalize.Main.getEventBaton(); + window.sandbox = toGlobalize.Main.getSandbox(); }); @@ -17429,7 +17594,7 @@ var setupZoomPoll = function(callback, context) { currentZoom = newZoom; callback.apply(context, [newZoom]); } - }, 100); + }, 500); }; exports.setupZoomPoll = setupZoomPoll; diff --git a/src/js/app/index.js b/src/js/app/index.js index 23f09d28..b58ea7bf 100644 --- a/src/js/app/index.js +++ b/src/js/app/index.js @@ -4,16 +4,13 @@ var Backbone = require('backbone'); var Constants = require('../util/constants'); var Views = require('../views'); var util = require('../util'); -var Command = require('../models/commandModel').Command; -var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall; -var DisabledMap = require('../level/disabledMap').DisabledMap; /** * Globals */ var events = _.clone(Backbone.Events); var commandUI; -var mainVis; +var sandbox; var eventBaton; /////////////////////////////////////////////////////////////////////// @@ -28,14 +25,12 @@ var init = function() { * - initializing the command input bar * - handling window.focus and zoom events **/ - var Visualization = require('../visuals/visualization').Visualization; + var Sandbox = require('../level/sandbox').Sandbox; var EventBaton = require('../util/eventBaton').EventBaton; eventBaton = new EventBaton(); commandUI = new CommandUI(); - mainVis = new Visualization({ - el: $('#canvasWrapper')[0] - }); + sandbox = new Sandbox(); // we always want to focus the text area to collect input var focusTextArea = function() { @@ -113,38 +108,23 @@ function CommandUI() { el: $('#commandLineHistory'), collection: this.commandCollection }); - - this.parseWaterfall = new ParseWaterfall(); - this.parseWaterfall.addFirst('instantWaterfall', new DisabledMap().getInstantCommands()); - - eventBaton.stealBaton('commandSubmitted', this.commandSubmitted, this); } -CommandUI.prototype.commandSubmitted = function(value) { - events.trigger('commandSubmittedPassive', value); - util.splitTextCommand(value, function(command) { - this.commandCollection.add(new Command({ - rawStr: command, - parseWaterfall: this.parseWaterfall - })); - }, this); -}; - exports.getEvents = function() { return events; }; -exports.getUI = function() { - return commandUI; -}; - -exports.getMainVis = function() { - return mainVis; +exports.getSandbox = function() { + return sandbox; }; exports.getEventBaton = function() { return eventBaton; }; +exports.getCommandUI = function() { + return commandUI; +} + exports.init = init; diff --git a/src/js/git/commands.js b/src/js/git/commands.js index 5fb4b512..aec83386 100644 --- a/src/js/git/commands.js +++ b/src/js/git/commands.js @@ -73,7 +73,7 @@ var parse = function(str) { options = str.slice(thisMethod.length + 1); method = thisMethod.slice('git '.length); } - }, this); + }); if (!method) { return false; diff --git a/src/js/git/index.js b/src/js/git/index.js index 71816c95..6020c2b9 100644 --- a/src/js/git/index.js +++ b/src/js/git/index.js @@ -1356,7 +1356,6 @@ GitEngine.prototype.dispatch = function(command, callback) { callback: whenDone }); - command.set('status', 'processing'); try { var methodName = command.get('method').replace(/-/g, '') + 'Starter'; this[methodName](); diff --git a/src/js/level/parseWaterfall.js b/src/js/level/parseWaterfall.js index f78443e3..1bdba026 100644 --- a/src/js/level/parseWaterfall.js +++ b/src/js/level/parseWaterfall.js @@ -16,7 +16,8 @@ function ParseWaterfall(options) { ]; this.parseWaterfall = options.parseWaterfall || [ - GitCommands.parse + GitCommands.parse, + SandboxCommands.parse ]; } diff --git a/src/js/level/sandboxCommands.js b/src/js/level/sandboxCommands.js index 4e35259b..88d22e65 100644 --- a/src/js/level/sandboxCommands.js +++ b/src/js/level/sandboxCommands.js @@ -39,4 +39,27 @@ var instantCommands = [ }] ]; +var regexMap = { + 'help': /^help($|\s)|\?/ +}; + +var parse = function(str) { + var sandboxMethod; + + _.each(regexMap, function(regex, method) { + if (regex.test(str)) { + sandboxMethod = method; + } + }); + + return (!sandboxMethod) ? false : { + toSet: { + eventName: 'processSandboxCommand', + method: sandboxMethod + } + }; +}; + exports.instantCommands = instantCommands; +exports.parse = parse; + diff --git a/src/js/models/collections.js b/src/js/models/collections.js index aff11efa..cf39845f 100644 --- a/src/js/models/collections.js +++ b/src/js/models/collections.js @@ -77,6 +77,8 @@ var CommandBuffer = Backbone.Model.extend({ }, processCommand: function(command) { + command.set('status', 'processing'); + var callback = _.bind(function() { this.setTimeout(); }, this); diff --git a/src/js/util/debug.js b/src/js/util/debug.js index 30e9057e..4128027c 100644 --- a/src/js/util/debug.js +++ b/src/js/util/debug.js @@ -25,7 +25,7 @@ _.each(toGlobalize, function(module) { $(document).ready(function() { window.events = toGlobalize.Main.getEvents(); - window.mainVis = toGlobalize.Main.getMainVis(); - window.ui = toGlobalize.Main.getMainVis(); + window.eventBaton = toGlobalize.Main.getEventBaton(); + window.sandbox = toGlobalize.Main.getSandbox(); }); diff --git a/src/js/util/zoomLevel.js b/src/js/util/zoomLevel.js index 71722038..084853ef 100644 --- a/src/js/util/zoomLevel.js +++ b/src/js/util/zoomLevel.js @@ -30,7 +30,7 @@ var setupZoomPoll = function(callback, context) { currentZoom = newZoom; callback.apply(context, [newZoom]); } - }, 100); + }, 500); }; exports.setupZoomPoll = setupZoomPoll;