pcottle.learnGitBranching/src/js/level/builder.js
2013-01-10 23:57:54 -08:00

344 lines
9.7 KiB
JavaScript

var _ = require('underscore');
var Backbone = require('backbone');
var Q = require('q');
var util = require('../util');
var Main = require('../app');
var Errors = require('../util/errors');
var Visualization = require('../visuals/visualization').Visualization;
var ParseWaterfall = require('../level/parseWaterfall').ParseWaterfall;
var Level = require('../level').Level;
var Command = require('../models/commandModel').Command;
var GitShim = require('../git/gitShim').GitShim;
var MultiView = require('../views/multiView').MultiView;
var CanvasTerminalHolder = require('../views').CanvasTerminalHolder;
var ConfirmCancelTerminal = require('../views').ConfirmCancelTerminal;
var NextLevelConfirm = require('../views').NextLevelConfirm;
var LevelToolbar = require('../views').LevelToolbar;
var MultiViewBuilder = require('../views/builderViews').MultiViewBuilder;
var regexMap = {
'define goal': /^define goal$/,
'help builder': /^help builder$/,
'define start': /^define start$/,
'edit dialog': /^edit dialog$/,
'show start': /^show start$/,
'hide start': /^hide start$/,
'define hint': /^define hint$/,
'finish': /^finish$/
};
var parse = util.genParseCommand(regexMap, 'processLevelBuilderCommand');
var LevelBuilder = Level.extend({
initialize: function(options) {
options = options || {};
options.level = options.level || {};
options.level.startDialog = {
childViews: require('../dialogs/levelBuilder').dialog
};
LevelBuilder.__super__.initialize.apply(this, [options]);
this.initStartVisualization();
this.startDialog = undefined;
this.definedGoal = false;
// we wont be using this stuff, and its to delete to ensure we overwrite all functions that
// include that functionality
delete this.treeCompare;
delete this.solved;
},
initName: function() {
this.levelToolbar = new LevelToolbar({
name: 'Level Builder'
});
},
initGoalData: function() {
// add some default behavior in the beginning
this.level.goalTreeString = '{"branches":{"master":{"target":"C1","id":"master"},"makeLevel":{"target":"C2","id":"makeLevel"}},"commits":{"C0":{"parents":[],"id":"C0","rootCommit":true},"C1":{"parents":["C0"],"id":"C1"},"C2":{"parents":["C1"],"id":"C2"}},"HEAD":{"target":"makeLevel","id":"HEAD"}}';
this.level.solutionCommand = 'git checkout -b makeLevel; git commit';
LevelBuilder.__super__.initGoalData.apply(this, arguments);
},
initStartVisualization: function() {
this.startCanvasHolder = new CanvasTerminalHolder({
additionalClass: 'startTree',
text: 'You can hide this window with "hide start"'
});
this.startVis = new Visualization({
el: this.startCanvasHolder.getCanvasLocation(),
containerElement: this.startCanvasHolder.getCanvasLocation(),
treeString: this.level.startTree,
noKeyboardInput: true,
noClick: true
});
},
startDie: function() {
this.startCanvasHolder.die();
this.startVis.die();
},
startOffCommand: function() {
Main.getEventBaton().trigger(
'commandSubmitted',
'echo "Get Building!!"'
);
},
initParseWaterfall: function(options) {
LevelBuilder.__super__.initParseWaterfall.apply(this, [options]);
this.parseWaterfall.addFirst(
'parseWaterfall',
parse
);
this.parseWaterfall.addFirst(
'instantWaterfall',
this.getInstantCommands()
);
},
buildLevel: function(command, deferred) {
this.exitLevel();
setTimeout(function() {
Main.getSandbox().buildLevel(command, deferred);
}, this.getAnimationTime() * 1.5);
},
getInstantCommands: function() {
return [
[/^help$|^\?$/, function() {
throw new Errors.CommandResult({
msg: 'You are in a level builder, so multiple forms of ' +
'help are available. Please select either ' +
'"help general" or "help builder"'
});
}]
];
},
takeControl: function() {
Main.getEventBaton().stealBaton('processLevelBuilderCommand', this.processLevelBuilderCommand, this);
LevelBuilder.__super__.takeControl.apply(this);
},
releaseControl: function() {
Main.getEventBaton().releaseBaton('processLevelBuilderCommand', this.processLevelBuilderCommand, this);
LevelBuilder.__super__.releaseControl.apply(this);
},
showGoal: function() {
this.startCanvasHolder.slideOut();
LevelBuilder.__super__.showGoal.apply(this, arguments);
},
showStart: function(command, deferred) {
this.goalCanvasHolder.slideOut();
this.startCanvasHolder.slideIn();
setTimeout(function() {
command.finishWith(deferred);
}, this.startCanvasHolder.getAnimationTime());
},
resetSolution: function() {
this.gitCommandsIssued = [];
this.level.solutionCommand = undefined;
},
hideStart: function(command, deferred) {
this.startCanvasHolder.slideOut();
setTimeout(function() {
command.finishWith(deferred);
}, this.startCanvasHolder.getAnimationTime());
},
defineStart: function(command, deferred) {
this.startDie();
command.addWarning(
'Defining start point... solution and goal will be overwritten if they were defined earlier'
);
this.resetSolution();
this.level.startTree = this.mainVis.gitEngine.printTree();
this.mainVis.resetFromThisTreeNow(this.level.startTree);
this.initStartVisualization();
this.showStart(command, deferred);
},
defineGoal: function(command, deferred) {
this.goalDie();
if (!this.gitCommandsIssued.length) {
command.set('error', new Errors.GitError({
msg: 'Your solution is empty!! something is amiss'
}));
deferred.resolve();
return;
}
this.definedGoal = true;
this.level.solutionCommand = this.gitCommandsIssued.join(';');
this.level.goalTreeString = this.mainVis.gitEngine.printTree();
this.initGoalVisualization();
this.showGoal(command, deferred);
},
defineHint: function(command, deferred) {
this.level.hint = prompt('Enter a hint! Or blank if you dont want one');
if (command) { command.finishWith(deferred); }
},
editDialog: function(command, deferred) {
var whenDoneEditing = Q.defer();
new MultiViewBuilder({
multiViewJSON: this.startDialog,
deferred: whenDoneEditing
});
whenDoneEditing.promise
.then(_.bind(function(levelObj) {
this.startDialog = levelObj;
}, this))
.fail(function() {
// nothing to do, they dont want to edit it apparently
})
.done(function() {
if (command) {
command.finishWith(deferred);
} else {
deferred.resolve();
}
});
},
finish: function(command, deferred) {
if (!this.gitCommandsIssued.length || !this.definedGoal) {
command.set('error', new Errors.GitError({
msg: 'Your solution is empty or goal is undefined!'
}));
deferred.resolve();
return;
}
var masterDeferred = Q.defer();
var chain = masterDeferred.promise;
if (this.level.hint === undefined) {
var askForHintDeferred = Q.defer();
chain = chain.then(function() {
return askForHintDeferred.promise;
});
// ask for a hint if there is none
var askForHintView = new ConfirmCancelTerminal({
markdowns: [
'You have not specified a hint, would you like to add one?'
]
});
askForHintView.getPromise()
.then(_.bind(this.defineHint, this))
.fail(_.bind(function() {
this.level.hint = '';
}, this))
.done(function() {
askForHintDeferred.resolve();
});
}
if (this.startDialog === undefined) {
var askForStartDeferred = Q.defer();
chain = chain.then(function() {
return askForStartDeferred.promise;
});
var askForStartView = new ConfirmCancelTerminal({
markdowns: [
'You have not specified a start dialog, would you like to add one?'
]
});
askForStartView.getPromise()
.then(_.bind(function() {
// oh boy this is complex
var whenEditedDialog = Q.defer();
// the undefined here is the command that doesnt need resolving just yet...
this.editDialog(undefined, whenEditedDialog);
return whenEditedDialog.promise;
}, this))
.fail(function() {
// if they dont want to edit the start dialog, do nothing
})
.done(function() {
askForStartDeferred.resolve();
});
}
chain = chain.done(_.bind(function() {
var compiledLevel = _.extend(
{},
this.level
);
// the start dialog now is just our help intro thing
delete compiledLevel.startDialog;
if (this.startDialog) {
compiledLevel.startDialog = this.startDialog;
}
console.log(compiledLevel);
console.log(this.startDialog);
command.finishWith(deferred);
}, this));
masterDeferred.resolve();
},
processLevelBuilderCommand: function(command, deferred) {
var methodMap = {
'define goal': this.defineGoal,
'define start': this.defineStart,
'show start': this.showStart,
'hide start': this.hideStart,
'finish': this.finish,
'define hint': this.defineHint,
'edit dialog': this.editDialog,
'help builder': LevelBuilder.__super__.startDialog
};
if (!methodMap[command.get('method')]) {
throw new Error('woah we dont support that method yet');
}
methodMap[command.get('method')].apply(this, arguments);
},
afterCommandDefer: function(defer, command) {
// we dont need to compare against the goal anymore
defer.resolve();
},
die: function() {
this.startDie();
LevelBuilder.__super__.die.apply(this, arguments);
delete this.startVis;
delete this.startCanvasHolder;
}
});
exports.LevelBuilder = LevelBuilder;