moving files

This commit is contained in:
Peter Cottle 2012-12-14 01:22:50 -08:00
parent 12b43a4c69
commit 4a9b21f2ce
7 changed files with 578 additions and 502 deletions

View file

@ -392,147 +392,10 @@ process.binding = function (name) {
});
require.define("/async.js",function(require,module,exports,__dirname,__filename,process,global){var GLOBAL = require('./constants').GLOBAL;
var Animation = Backbone.Model.extend({
defaults: {
duration: 300,
closure: null
},
validateAtInit: function() {
if (!this.get('closure')) {
throw new Error('give me a closure!');
}
},
initialize: function(options) {
this.validateAtInit();
},
run: function() {
this.get('closure')();
}
});
var AnimationQueue = Backbone.Model.extend({
defaults: {
animations: null,
index: 0,
callback: null,
defer: false
},
initialize: function(options) {
this.set('animations', []);
if (!options.callback) {
console.warn('no callback');
}
},
add: function(animation) {
if (!animation instanceof Animation) {
throw new Error("Need animation not something else");
}
this.get('animations').push(animation);
},
start: function() {
this.set('index', 0);
// set the global lock that we are animating
GLOBAL.isAnimating = true;
this.next();
},
finish: function() {
// release lock here
GLOBAL.isAnimating = false;
this.get('callback')();
},
next: function() {
// ok so call the first animation, and then set a timeout to call the next
// TODO: animations with callbacks!!
var animations = this.get('animations');
var index = this.get('index');
if (index >= animations.length) {
this.finish();
return;
}
var next = animations[index];
var duration = next.get('duration');
next.run();
this.set('index', index + 1);
setTimeout(_.bind(function() {
this.next();
}, this), duration);
}
});
exports.Animation = Animation;
exports.AnimationQueue = AnimationQueue;
});
require.define("/constants.js",function(require,module,exports,__dirname,__filename,process,global){/**
* Constants....!!!
*/
var TIME = {
betweenCommandsDelay: 400
};
// useful for locks, etc
var GLOBAL = {
isAnimating: false
};
var GRAPHICS = {
arrowHeadSize: 8,
nodeRadius: 17,
curveControlPointOffset: 50,
defaultEasing: 'easeInOut',
defaultAnimationTime: 400,
//rectFill: '#FF3A3A',
rectFill: 'hsb(0.8816909813322127,0.7,1)',
headRectFill: '#2831FF',
rectStroke: '#FFF',
rectStrokeWidth: '3',
multiBranchY: 20,
upstreamHeadOpacity: 0.5,
upstreamNoneOpacity: 0.2,
edgeUpstreamHeadOpacity: 0.4,
edgeUpstreamNoneOpacity: 0.15,
visBranchStrokeWidth: 2,
visBranchStrokeColorNone: '#333',
defaultNodeFill: 'hsba(0.5,0.8,0.7,1)',
defaultNodeStrokeWidth: 2,
defaultNodeStroke: '#FFF',
orphanNodeFill: 'hsb(0.5,0.8,0.7)'
};
exports.GLOBAL = GLOBAL;
exports.TIME = TIME;
exports.GRAPHICS = GRAPHICS;
});
require.define("/git.js",function(require,module,exports,__dirname,__filename,process,global){var AnimationFactoryModule = require('./animationFactory');
require.define("/git.js",function(require,module,exports,__dirname,__filename,process,global){var AnimationFactoryModule = require('./animation/animationFactory');
var animationFactory = new AnimationFactoryModule.AnimationFactory();
var Main = require('./app/main');
var AnimationQueue = require('./async').AnimationQueue;
var AnimationQueue = require('./animation').AnimationQueue;
var InteractiveRebaseView = require('./miscViews').InteractiveRebaseView;
var Errors = require('./errors');
@ -2179,7 +2042,7 @@ exports.Ref = Ref;
});
require.define("/animationFactory.js",function(require,module,exports,__dirname,__filename,process,global){/******************
require.define("/animation/animationFactory.js",function(require,module,exports,__dirname,__filename,process,global){/******************
* This class is responsible for a lot of the heavy lifting around creating an animation at a certain state in time.
* The tricky thing is that when a new commit has to be "born," say in the middle of a rebase
* or something, it must animate out from the parent position to it's birth position.
@ -2189,8 +2052,8 @@ require.define("/animationFactory.js",function(require,module,exports,__dirname,
* and then essentially animate the entire tree too.
*/
var Animation = require('./async').Animation;
var GRAPHICS = require('./constants').GRAPHICS;
var Animation = require('./index').Animation;
var GRAPHICS = require('../constants').GRAPHICS;
// essentially a static class
var AnimationFactory = function() {
@ -2436,6 +2299,143 @@ AnimationFactory.prototype.genFromToSnapshotAnimation = function(
exports.AnimationFactory = AnimationFactory;
});
require.define("/animation/index.js",function(require,module,exports,__dirname,__filename,process,global){var GLOBAL = require('../constants').GLOBAL;
var Animation = Backbone.Model.extend({
defaults: {
duration: 300,
closure: null
},
validateAtInit: function() {
if (!this.get('closure')) {
throw new Error('give me a closure!');
}
},
initialize: function(options) {
this.validateAtInit();
},
run: function() {
this.get('closure')();
}
});
var AnimationQueue = Backbone.Model.extend({
defaults: {
animations: null,
index: 0,
callback: null,
defer: false
},
initialize: function(options) {
this.set('animations', []);
if (!options.callback) {
console.warn('no callback');
}
},
add: function(animation) {
if (!animation instanceof Animation) {
throw new Error("Need animation not something else");
}
this.get('animations').push(animation);
},
start: function() {
this.set('index', 0);
// set the global lock that we are animating
GLOBAL.isAnimating = true;
this.next();
},
finish: function() {
// release lock here
GLOBAL.isAnimating = false;
this.get('callback')();
},
next: function() {
// ok so call the first animation, and then set a timeout to call the next
// TODO: animations with callbacks!!
var animations = this.get('animations');
var index = this.get('index');
if (index >= animations.length) {
this.finish();
return;
}
var next = animations[index];
var duration = next.get('duration');
next.run();
this.set('index', index + 1);
setTimeout(_.bind(function() {
this.next();
}, this), duration);
}
});
exports.Animation = Animation;
exports.AnimationQueue = AnimationQueue;
});
require.define("/constants.js",function(require,module,exports,__dirname,__filename,process,global){/**
* Constants....!!!
*/
var TIME = {
betweenCommandsDelay: 400
};
// useful for locks, etc
var GLOBAL = {
isAnimating: false
};
var GRAPHICS = {
arrowHeadSize: 8,
nodeRadius: 17,
curveControlPointOffset: 50,
defaultEasing: 'easeInOut',
defaultAnimationTime: 400,
//rectFill: '#FF3A3A',
rectFill: 'hsb(0.8816909813322127,0.7,1)',
headRectFill: '#2831FF',
rectStroke: '#FFF',
rectStrokeWidth: '3',
multiBranchY: 20,
upstreamHeadOpacity: 0.5,
upstreamNoneOpacity: 0.2,
edgeUpstreamHeadOpacity: 0.4,
edgeUpstreamNoneOpacity: 0.15,
visBranchStrokeWidth: 2,
visBranchStrokeColorNone: '#333',
defaultNodeFill: 'hsba(0.5,0.8,0.7,1)',
defaultNodeStrokeWidth: 2,
defaultNodeStroke: '#FFF',
orphanNodeFill: 'hsb(0.5,0.8,0.7)'
};
exports.GLOBAL = GLOBAL;
exports.TIME = TIME;
exports.GRAPHICS = GRAPHICS;
});
require.define("/app/main.js",function(require,module,exports,__dirname,__filename,process,global){/**
@ -5306,355 +5306,6 @@ exports.LevelEngine = LevelEngine;
});
require.define("/animationFactory.js",function(require,module,exports,__dirname,__filename,process,global){/******************
* This class is responsible for a lot of the heavy lifting around creating an animation at a certain state in time.
* The tricky thing is that when a new commit has to be "born," say in the middle of a rebase
* or something, it must animate out from the parent position to it's birth position.
* These two positions though may not be where the commit finally ends up. So we actually need to take a snapshot of the tree,
* store all those positions, take a snapshot of the tree after a layout refresh afterwards, and then animate between those two spots.
* and then essentially animate the entire tree too.
*/
var Animation = require('./async').Animation;
var GRAPHICS = require('./constants').GRAPHICS;
// essentially a static class
var AnimationFactory = function() {
};
AnimationFactory.prototype.genCommitBirthAnimation = function(animationQueue, commit, gitVisuals) {
if (!animationQueue) {
throw new Error("Need animation queue to add closure to!");
}
var time = GRAPHICS.defaultAnimationTime * 1.0;
var bounceTime = time * 2;
// essentially refresh the entire tree, but do a special thing for the commit
var visNode = commit.get('visNode');
var animation = function() {
// this takes care of refs and all that jazz, and updates all the positions
gitVisuals.refreshTree(time);
visNode.setBirth();
visNode.parentInFront();
gitVisuals.visBranchesFront();
visNode.animateUpdatedPosition(bounceTime, 'bounce');
visNode.animateOutgoingEdges(time);
};
animationQueue.add(new Animation({
closure: animation,
duration: Math.max(time, bounceTime)
}));
};
AnimationFactory.prototype.overrideOpacityDepth2 = function(attr, opacity) {
opacity = (opacity === undefined) ? 1 : opacity;
var newAttr = {};
_.each(attr, function(partObj, partName) {
newAttr[partName] = {};
_.each(partObj, function(val, key) {
if (key == 'opacity') {
newAttr[partName][key] = opacity;
} else {
newAttr[partName][key] = val;
}
});
});
return newAttr;
};
AnimationFactory.prototype.overrideOpacityDepth3 = function(snapShot, opacity) {
var newSnap = {};
_.each(snapShot, function(visObj, visID) {
newSnap[visID] = this.overrideOpacityDepth2(visObj, opacity);
}, this);
return newSnap;
};
AnimationFactory.prototype.genCommitBirthClosureFromSnapshot = function(step, gitVisuals) {
var time = GRAPHICS.defaultAnimationTime * 1.0;
var bounceTime = time * 1.5;
var visNode = step.newCommit.get('visNode');
var afterAttrWithOpacity = this.overrideOpacityDepth2(step.afterSnapshot[visNode.getID()]);
var afterSnapWithOpacity = this.overrideOpacityDepth3(step.afterSnapshot);
var animation = function() {
visNode.setBirthFromSnapshot(step.beforeSnapshot);
visNode.parentInFront();
gitVisuals.visBranchesFront();
visNode.animateToAttr(afterAttrWithOpacity, bounceTime, 'bounce');
visNode.animateOutgoingEdgesToAttr(afterSnapWithOpacity, bounceTime);
};
return animation;
};
AnimationFactory.prototype.refreshTree = function(animationQueue, gitVisuals) {
animationQueue.add(new Animation({
closure: function() {
gitVisuals.refreshTree();
}
}));
};
AnimationFactory.prototype.rebaseAnimation = function(animationQueue, rebaseResponse,
gitEngine, gitVisuals) {
this.rebaseHighlightPart(animationQueue, rebaseResponse, gitEngine);
this.rebaseBirthPart(animationQueue, rebaseResponse, gitEngine, gitVisuals);
};
AnimationFactory.prototype.rebaseHighlightPart = function(animationQueue, rebaseResponse, gitEngine) {
var fullTime = GRAPHICS.defaultAnimationTime * 0.66;
var slowTime = fullTime * 2.0;
// we want to highlight all the old commits
var oldCommits = rebaseResponse.toRebaseArray;
// we are either highlighting to a visBranch or a visNode
var visBranch = rebaseResponse.destinationBranch.get('visBranch');
if (!visBranch) {
// in the case where we rebase onto a commit
visBranch = rebaseResponse.destinationBranch.get('visNode');
}
_.each(oldCommits, function(oldCommit) {
var visNode = oldCommit.get('visNode');
animationQueue.add(new Animation({
closure: function() {
visNode.highlightTo(visBranch, slowTime, 'easeInOut');
},
duration: fullTime * 1.5
}));
}, this);
this.delay(animationQueue, fullTime * 2);
};
AnimationFactory.prototype.rebaseBirthPart = function(animationQueue, rebaseResponse,
gitEngine, gitVisuals) {
var rebaseSteps = rebaseResponse.rebaseSteps;
var newVisNodes = [];
_.each(rebaseSteps, function(step) {
var visNode = step.newCommit.get('visNode');
newVisNodes.push(visNode);
visNode.setOpacity(0);
visNode.setOutgoingEdgesOpacity(0);
}, this);
var previousVisNodes = [];
_.each(rebaseSteps, function(rebaseStep, index) {
var toOmit = newVisNodes.slice(index + 1);
var snapshotPart = this.genFromToSnapshotAnimation(
rebaseStep.beforeSnapshot,
rebaseStep.afterSnapshot,
toOmit,
previousVisNodes,
gitVisuals
);
var birthPart = this.genCommitBirthClosureFromSnapshot(rebaseStep, gitVisuals);
var animation = function() {
snapshotPart();
birthPart();
};
animationQueue.add(new Animation({
closure: animation,
duration: GRAPHICS.defaultAnimationTime * 1.5
}));
previousVisNodes.push(rebaseStep.newCommit.get('visNode'));
}, this);
// need to delay to let bouncing finish
this.delay(animationQueue);
this.refreshTree(animationQueue, gitVisuals);
};
AnimationFactory.prototype.delay = function(animationQueue, time) {
time = time || GRAPHICS.defaultAnimationTime;
animationQueue.add(new Animation({
closure: function() { },
duration: time
}));
};
AnimationFactory.prototype.genSetAllCommitOpacities = function(visNodes, opacity) {
// need to slice for closure
var nodesToAnimate = visNodes.slice(0);
return function() {
_.each(nodesToAnimate, function(visNode) {
visNode.setOpacity(opacity);
visNode.setOutgoingEdgesOpacity(opacity);
});
};
};
AnimationFactory.prototype.stripObjectsFromSnapshot = function(snapShot, toOmit) {
var ids = [];
_.each(toOmit, function(obj) {
ids.push(obj.getID());
});
var newSnapshot = {};
_.each(snapShot, function(val, key) {
if (_.include(ids, key)) {
// omit
return;
}
newSnapshot[key] = val;
}, this);
return newSnapshot;
};
AnimationFactory.prototype.genFromToSnapshotAnimation = function(
beforeSnapshot,
afterSnapshot,
commitsToOmit,
commitsToFixOpacity,
gitVisuals) {
// we want to omit the commit outgoing edges
var toOmit = [];
_.each(commitsToOmit, function(visNode) {
toOmit.push(visNode);
toOmit = toOmit.concat(visNode.get('outgoingEdges'));
});
var fixOpacity = function(obj) {
if (!obj) { return; }
_.each(obj, function(attr, partName) {
obj[partName].opacity = 1;
});
};
// HORRIBLE loop to fix opacities all throughout the snapshot
_.each([beforeSnapshot, afterSnapshot], function(snapShot) {
_.each(commitsToFixOpacity, function(visNode) {
fixOpacity(snapShot[visNode.getID()]);
_.each(visNode.get('outgoingEdges'), function(visEdge) {
fixOpacity(snapShot[visEdge.getID()]);
});
});
});
return function() {
gitVisuals.animateAllFromAttrToAttr(beforeSnapshot, afterSnapshot, toOmit);
};
};
exports.AnimationFactory = AnimationFactory;
});
require("/animationFactory.js");
require.define("/async.js",function(require,module,exports,__dirname,__filename,process,global){var GLOBAL = require('./constants').GLOBAL;
var Animation = Backbone.Model.extend({
defaults: {
duration: 300,
closure: null
},
validateAtInit: function() {
if (!this.get('closure')) {
throw new Error('give me a closure!');
}
},
initialize: function(options) {
this.validateAtInit();
},
run: function() {
this.get('closure')();
}
});
var AnimationQueue = Backbone.Model.extend({
defaults: {
animations: null,
index: 0,
callback: null,
defer: false
},
initialize: function(options) {
this.set('animations', []);
if (!options.callback) {
console.warn('no callback');
}
},
add: function(animation) {
if (!animation instanceof Animation) {
throw new Error("Need animation not something else");
}
this.get('animations').push(animation);
},
start: function() {
this.set('index', 0);
// set the global lock that we are animating
GLOBAL.isAnimating = true;
this.next();
},
finish: function() {
// release lock here
GLOBAL.isAnimating = false;
this.get('callback')();
},
next: function() {
// ok so call the first animation, and then set a timeout to call the next
// TODO: animations with callbacks!!
var animations = this.get('animations');
var index = this.get('index');
if (index >= animations.length) {
this.finish();
return;
}
var next = animations[index];
var duration = next.get('duration');
next.run();
this.set('index', index + 1);
setTimeout(_.bind(function() {
this.next();
}, this), duration);
}
});
exports.Animation = Animation;
exports.AnimationQueue = AnimationQueue;
});
require("/async.js");
require.define("/collections.js",function(require,module,exports,__dirname,__filename,process,global){var Commit = require('./git').Commit;
var Branch = require('./git').Branch;
@ -6217,8 +5868,8 @@ require.define("/debug.js",function(require,module,exports,__dirname,__filename,
Levels: require('./levels'),
Constants: require('./constants'),
Collections: require('./collections'),
Async: require('./async'),
AnimationFactory: require('./animationFactory'),
Async: require('./animation'),
AnimationFactory: require('./animation/animationFactory'),
Main: require('./app/main')
};
@ -6281,10 +5932,10 @@ var GitError = exports.GitError = MyError.extend({
});
require("/errors.js");
require.define("/git.js",function(require,module,exports,__dirname,__filename,process,global){var AnimationFactoryModule = require('./animationFactory');
require.define("/git.js",function(require,module,exports,__dirname,__filename,process,global){var AnimationFactoryModule = require('./animation/animationFactory');
var animationFactory = new AnimationFactoryModule.AnimationFactory();
var Main = require('./app/main');
var AnimationQueue = require('./async').AnimationQueue;
var AnimationQueue = require('./animation').AnimationQueue;
var InteractiveRebaseView = require('./miscViews').InteractiveRebaseView;
var Errors = require('./errors');

View file

@ -8,8 +8,8 @@
* and then essentially animate the entire tree too.
*/
var Animation = require('./async').Animation;
var GRAPHICS = require('./constants').GRAPHICS;
var Animation = require('./index').Animation;
var GRAPHICS = require('../constants').GRAPHICS;
// essentially a static class
var AnimationFactory = function() {

View file

@ -1,4 +1,4 @@
var GLOBAL = require('./constants').GLOBAL;
var GLOBAL = require('../constants').GLOBAL;
var Animation = Backbone.Model.extend({
defaults: {

53
src/app/main.js Normal file
View file

@ -0,0 +1,53 @@
/**
* Globals
*/
var events = _.clone(Backbone.Events);
var ui = null;
var mainVis = null;
///////////////////////////////////////////////////////////////////////
$(document).ready(function(){
var Visuals = require('../visuals');
ui = new UI();
mainVis = new Visuals.Visualization({
el: $('#canvasWrapper')[0]
});
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 master; git checkout master; gc; gc; git merge bugFix");
}, 500);
}
});
function UI() {
var Collections = require('../collections');
var CommandViews = require('../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();
}
exports.getEvents = function() {
return events;
};
exports.getUI = function() {
return ui;
};

View file

@ -6,8 +6,8 @@ var toGlobalize = {
Levels: require('./levels'),
Constants: require('./constants'),
Collections: require('./collections'),
Async: require('./async'),
AnimationFactory: require('./animationFactory'),
Async: require('./animation'),
AnimationFactory: require('./animation/animationFactory'),
Main: require('./app/main')
};

View file

@ -1,7 +1,7 @@
var AnimationFactoryModule = require('./animationFactory');
var AnimationFactoryModule = require('./animation/animationFactory');
var animationFactory = new AnimationFactoryModule.AnimationFactory();
var Main = require('./app/main');
var AnimationQueue = require('./async').AnimationQueue;
var AnimationQueue = require('./animation').AnimationQueue;
var InteractiveRebaseView = require('./miscViews').InteractiveRebaseView;
var Errors = require('./errors');

372
src/models/commandModel.js Normal file
View file

@ -0,0 +1,372 @@
var Errors = require('../errors');
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: '',
error: null,
warnings: null,
generalArgs: null,
supportedMap: null,
options: null,
method: null,
createTime: null
},
validateAtInit: function() {
// weird things happen with defaults if you dont
// make new objects
this.set('generalArgs', []);
this.set('supportedMap', {});
this.set('warnings', []);
if (this.get('rawStr') === null) {
throw new Error('Give me a string!');
}
if (!this.get('createTime')) {
this.set('createTime', new Date().toString());
}
this.on('change:error', this.errorChanged, this);
// catch errors on init
if (this.get('error')) {
this.errorChanged();
}
},
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 = '<i class="icon-exclamation-sign"></i>';
return '<p>' + i + this.get('warnings').join('</p><p>' + i) + '</p>';
},
initialize: function() {
this.validateAtInit();
this.parseOrCatch();
},
parseOrCatch: function() {
try {
this.parse();
} catch (err) {
if (err instanceof CommandProcessError ||
err instanceof GitError ||
err instanceof CommandResult ||
err instanceof Warning) {
// errorChanged() will handle status and all of that
this.set('error', err);
} else {
throw err;
}
}
},
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());
},
getShortcutMap: function() {
return {
'git commit': /^gc($|\s)/,
'git add': /^ga($|\s)/,
'git checkout': /^gchk($|\s)/,
'git rebase': /^gr($|\s)/,
'git branch': /^gb($|\s)/
};
},
getRegexMap: function() {
return {
// ($|\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)/
};
},
getSandboxCommands: function() {
return [
[/^ls/, function() {
throw new CommandResult({
msg: "DontWorryAboutFilesInThisDemo.txt"
});
}],
[/^cd/, function() {
throw new CommandResult({
msg: "Directory Changed to '/directories/dont/matter/in/this/demo'"
});
}],
[/^git help($|\s)/, function() {
// sym link this to the blank git command
var allCommands = Command.prototype.getSandboxCommands();
// wow this is hacky :(
var equivalent = 'git';
_.each(allCommands, function(bits) {
var regex = bits[0];
if (regex.test(equivalent)) {
bits[1]();
}
});
}],
[/^git$/, function() {
var lines = [
'Git Version PCOTTLE.1.0',
'<br/>',
'Usage:',
_.escape('\t git <command> [<args>]'),
'<br/>',
'Supported commands:',
'<br/>'
];
var commands = OptionParser.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, '&nbsp;&nbsp;&nbsp;');
throw new CommandResult({
msg: msg
});
}],
[/^refresh$/, function() {
var events = require('../app/main').getEvents();
events.trigger('refreshTree');
throw new CommandResult({
msg: "Refreshing tree..."
});
}],
[/^rollup (\d+)$/, function(bits) {
var events = require('../app/main').getEvents();
// go roll up these commands by joining them with semicolons
events.trigger('rollupCommands', bits[1]);
throw new CommandResult({
msg: 'Commands combined!'
});
}]
];
},
parse: 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 check if it's one of our sandbox commands
_.each(this.getSandboxCommands(), function(tuple) {
var regex = tuple[0];
var results = regex.exec(str);
if (results) {
tuple[1](results);
}
});
// then check if shortcut exists, and replace, but
// preserve options if so
_.each(this.getShortcutMap(), function(regex, method) {
var results = regex.exec(str);
if (results) {
str = method + ' ' + str.slice(results[0].length);
}
});
// see if begins with git
if (str.slice(0,3) !== 'git') {
throw new CommandProcessError({
msg: 'That command is not supported, sorry!'
});
}
// ok, we have a (probably) valid command. actually parse it
this.gitParse(str);
},
gitParse: function(str) {
// now slice off command part
var fullCommand = str.slice('git '.length);
// see if we support this particular command
_.each(this.getRegexMap(), function(regex, method) {
if (regex.exec(fullCommand)) {
this.set('options', fullCommand.slice(method.length + 1));
this.set('method', method);
// we should stop iterating, but the regex will only match
// one command in practice. we could stop iterating if we used
// jqeurys for each but im using underscore (for no real reason other
// than style)
}
}, this);
if (!this.get('method')) {
throw new CommandProcessError({
msg: "Sorry, this demo does not support that git command: " + fullCommand
});
}
// parse off the options and assemble the map / general args
var optionParser = new OptionParser(this.get('method'), this.get('options'));
// steal these away so we can be completely JSON
this.set('generalArgs', optionParser.generalArgs);
this.set('supportedMap', optionParser.supportedMap);
}
});
/**
* OptionParser
*/
function OptionParser(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();
}
OptionParser.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: {}
};
};
OptionParser.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);
}
}
// done!
};
// command entry is for the commandview
var CommandEntry = Backbone.Model.extend({
defaults: {
text: ''
},
localStorage: new Backbone.LocalStorage('CommandEntries')
});
exports.CommandEntry = CommandEntry;
exports.Command = Command;