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;