diff --git a/src/animationFactory.js b/src/animationFactory.js new file mode 100644 index 00000000..0fc5fc10 --- /dev/null +++ b/src/animationFactory.js @@ -0,0 +1,13 @@ +/****************** + * 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. + + * not sure if this is necessary yet, so ill hold off for now. lets do some refs + + */ + diff --git a/src/async.js b/src/async.js index b528991c..f4e36cfa 100644 --- a/src/async.js +++ b/src/async.js @@ -71,3 +71,4 @@ var AnimationQueue = Backbone.Model.extend({ }, this), duration); }, }); + diff --git a/src/commandModel.js b/src/commandModel.js index 1d450c74..cb33ed61 100644 --- a/src/commandModel.js +++ b/src/commandModel.js @@ -1,24 +1,52 @@ var Command = Backbone.Model.extend({ defaults: { status: 'inqueue', + rawStr: null, result: '', + error: null, - generalArgs: [], - supportedMap: {}, + warnings: null, + + generalArgs: null, + supportedMap: null, options: null, method: null, - createTime: null, - rawStr: 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(); + } + }, + + addWarning: function(msg) { + this.set('warnings', this.get('warnings').push(msg)); + }, + + getFormattedWarnings: function() { + if (!this.get('warnings').length) { + return ''; + } + + return '

' + this.get('warnings').join('

') + '

'; }, initialize: function() { @@ -32,8 +60,9 @@ var Command = Backbone.Model.extend({ } catch (err) { if (err instanceof CommandProcessError || err instanceof GitError || - err instanceof CommandResult) { - // erroChanged() will handle status and all of that + err instanceof CommandResult || + err instanceof Warning) { + // errorChanged() will handle status and all of that this.set('error', err); } else { throw err; @@ -41,12 +70,15 @@ var Command = Backbone.Model.extend({ } }, - errorChanged: function(model, 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(); }, @@ -76,6 +108,7 @@ var Command = Backbone.Model.extend({ reset: /^reset($|\s)/, branch: /^branch($|\s)/, revert: /^revert($|\s)/, + log: /^log($|\s)/, merge: /^merge($|\s)/ }; }, @@ -204,6 +237,7 @@ OptionParser.prototype.getMasterOptionMap = function() { '-a': false, // warning '-am': false }, + log: {}, add: {}, branch: { '-d': false, diff --git a/src/commandViews.js b/src/commandViews.js index 117eb258..8f8b2f8e 100644 --- a/src/commandViews.js +++ b/src/commandViews.js @@ -139,11 +139,11 @@ var CommandView = Backbone.View.extend({ var json = _.extend( { resultType: '', - result: '' + result: '', + warnings: '' }, this.model.toJSON() ); - console.log('rendering', this.model.toJSON()); this.$el.html(this.template(json)); return this; @@ -164,6 +164,22 @@ var CommandLineHistoryView = Backbone.View.extend({ this.collection.on('all', this.render, this); this.collection.on('change', this.scrollDown, this); + + events.on('issueWarning', this.addWarning, this); + }, + + addWarning: function(msg) { + console.log('here', arguments); + var err = new Warning({ + msg: msg + }); + + var command = new Command({ + error: err, + rawStr: 'Warning:' + }); + + this.collection.add(command); }, scrollDown: function() { diff --git a/src/errors.js b/src/errors.js index 0e3b3be6..83e5e299 100644 --- a/src/errors.js +++ b/src/errors.js @@ -12,7 +12,7 @@ var MyError = Backbone.Model.extend({ }, toResult: function() { - return this.get('msg').replace('\n', '
'); + return '

' + this.get('msg').replace(/\n/g, '

') + '

'; } }); @@ -28,6 +28,12 @@ var CommandResult = MyError.extend({ } }); +var Warning = MyError.extend({ + defaults: { + type: 'Warning' + } +}); + var GitError = MyError.extend({ defaults: { type: 'Git Error' diff --git a/src/git.js b/src/git.js index b2152614..45c61514 100644 --- a/src/git.js +++ b/src/git.js @@ -40,12 +40,6 @@ GitEngine.prototype.init = function() { // commit once to get things going this.commit(); - - // update tree - // TODO make async, not async - setTimeout(function() { - events.trigger('treeRefresh'); - }, 100); }; GitEngine.prototype.getDetachedHead = function() { @@ -193,7 +187,7 @@ GitEngine.prototype.resetStarter = function() { }); } if (this.commandOptions['--hard']) { - events.trigger('commandProcessWarn', + this.command.addWarning( 'Nice! You are using --hard. The default behavior is a hard reset in ' + "this demo, so don't worry about specifying the option explicity" ); @@ -220,10 +214,10 @@ GitEngine.prototype.reset = function(target) { GitEngine.prototype.commitStarter = function() { this.acceptNoGeneralArgs(); if (this.commandOptions['-a']) { - events.trigger('commandProcessWarn', 'No need to add files in this demo'); + this.command.addWarning('No need to add files in this demo'); } if (this.commandOptions['-am']) { - events.trigger('commandProcessWarn', "Don't worry about adding files or commit messages in this demo"); + this.command.addWarning("Don't worry about adding files or commit messages in this demo"); } this.commit(); }; @@ -237,7 +231,7 @@ GitEngine.prototype.commit = function() { var newCommit = this.makeCommit([targetCommit]); if (this.getDetachedHead()) { - events.trigger('commandProcessWarn', 'Warning!! Detached HEAD state'); + this.command.addWarning('Warning!! Detached HEAD state'); this.HEAD.set('target', newCommit); } else { var targetBranch = this.HEAD.get('target'); @@ -611,7 +605,7 @@ GitEngine.prototype.branchStarter = function() { // handle deletion first if (this.commandOptions['-d'] || this.commandOptions['-D']) { var names = this.commandOptions['-d']; - names.concat(this.commandOptions['-D']); + names = names.concat(this.commandOptions['-D']); if (!names.length) { throw new GitError({ msg: 'I expect branch names when deleting' @@ -726,9 +720,52 @@ GitEngine.prototype.dispatch = function(command, callback) { this.animationQueue.add(new Animation({closure: function() { console.log(Math.random()); }})); } + // animation queue will call the callback when its done this.animationQueue.start(); }; +GitEngine.prototype.logStarter = function() { + if (this.generalArgs.length > 1) { + throw new GitError({ + msg: "git log with more than 1 argument doesn't make sense" + }); + } + if (this.generalArgs.length == 0) { + this.generalArgs.push('HEAD'); + } + this.log(this.generalArgs[0]); +}; + +GitEngine.prototype.log = function(ref) { + // first get the commit we referenced + var commit = this.getCommitFromRef(ref); + + // then get as many far back as we can from here, order by commit date + var toDump = []; + var pQueue = [commit]; + + while (pQueue.length) { + var popped = pQueue.shift(0); + toDump.push(popped); + + if (popped.get('parents') && popped.get('parents').length) { + pQueue = pQueue.concat(popped.get('parents')); + } + pQueue.sort(this.idSortFunc); + } + + toDump.reverse(); + // now go through and collect logs + var bigLogStr = ''; + _.each(toDump, function(c) { + bigLogStr += c.getLogEntry(); + }, this); + + throw new CommandResult({ + msg: bigLogStr + }); +}; + GitEngine.prototype.addStarter = function() { throw new CommandResult({ msg: "This demo is meant to demonstrate git branching, so don't worry about " + @@ -750,7 +787,7 @@ GitEngine.prototype.getCommonAncestor = function(ancestor, cousin) { if (upstreamSet[here.get('id')]) { return here; } - queue.concat(here.get('parents')); + queue = queue.concat(here.get('parents')); } throw new Error('something has gone very wrong... two nodes arent connected!'); }; @@ -814,12 +851,32 @@ var Commit = Backbone.Model.extend({ type: 'commit', children: null, parents: null, + author: 'Peter Cottle', + createTime: null, + commitMessage: null + }, + + getLogEntry: function() { + // for now we are just joining all these things with newlines... + return [ + 'Author: ' + this.get('author'), + 'Date: ' + this.get('createTime'), + this.get('commitMessage'), + 'Commit: ' + this.get('id') + ].join('\n' ) + '\n'; }, validateAtInit: function() { if (!this.get('id')) { this.set('id', uniqueId('C')); } + if (!this.get('createTime')) { + this.set('createTime', new Date().toString()); + } + if (!this.get('commitMessage')) { + this.set('commitMessage', 'Quick Commit. Go Bears!'); + } + this.set('children', []); // root commits have no parents diff --git a/src/index.html b/src/index.html index 87f09e1a..8b0c2b61 100644 --- a/src/index.html +++ b/src/index.html @@ -74,7 +74,15 @@

- <%= result %> +

+ <%= result %> +
+

+ +

+

+ <%= warnings %> +

@@ -92,11 +100,11 @@ - + diff --git a/src/style/main.css b/src/style/main.css index d7701d24..cdb9688f 100644 --- a/src/style/main.css +++ b/src/style/main.css @@ -195,11 +195,17 @@ p.commandLine span.icons i { p.commandLine.inqueue span.icons i.icon-check-empty, p.commandLine.error span.icons i.icon-exclamation-sign, +p.commandLine.warning span.icons i.icon-exclamation-sign, p.commandLine.processing span.icons i.icon-retweet, p.commandLine.finished span.icons i.icon-check { opacity: 1; } +p.commandLine.warning span.icons { + background-color: #E0FF00; + color: black; +} + p.commandLine.inqueue span.icons { background-color: #EBEB24; color: black; diff --git a/src/tree.js b/src/tree.js index cc3c2e54..c2e10e30 100644 --- a/src/tree.js +++ b/src/tree.js @@ -1,6 +1,9 @@ var VisBranch = Backbone.Model.extend({ defaults: { - pos: null + pos: null, + text: null, + animationSpeed: GRAPHICS.defaultAnimationTime, + animationEasing: GRAPHICS.defaultEasing }, validateAtInit: function() { @@ -37,20 +40,14 @@ var VisBranch = Backbone.Model.extend({ this.set('text', text); }, - animateUpdatedPos: function(paper) { + animateUpdatedPos: function(speed, easing) { var pos = this.getPosition(); - var t = this.get('text'); - if (!t) { - this.genGraphics(paper); - t = this.get('text'); - // TODO HACKY - } this.get('text').toFront().stop().animate({ x: pos.x, y: pos.y }, - 300, - 'easeInOut' + speed || this.get('animationSpeed'), + easing || this.get('animationEasing') ); } }); diff --git a/src/visuals.js b/src/visuals.js index f655787e..cda3a7c4 100644 --- a/src/visuals.js +++ b/src/visuals.js @@ -71,7 +71,7 @@ GitVisuals.prototype.toScreenCoords = function(pos) { **************************************/ GitVisuals.prototype.refreshTree = function() { - if (!this.paperReady) { return; } + if (!this.paperReady) { console.warn('called refresh tree when not ready yet'); return; } this.calculateTreeCoords(); this.animateNodePositions(); @@ -79,6 +79,13 @@ GitVisuals.prototype.refreshTree = function() { this.animateRefs(); }; +GitVisuals.prototype.refreshTreeHarsh = function() { + this.calculateTreeCoords(); + + this.animateEdges(); + this.animateNodePositions(); +}; + GitVisuals.prototype.calculateTreeCoords = function() { if (!this.rootCommit) { throw new Error('grr, no root commit!'); @@ -142,6 +149,13 @@ GitVisuals.prototype.assignBoundsRecursive = function(commit, min, max) { GitVisuals.prototype.calcDepth = function() { var maxDepth = this.calcDepthRecursive(this.rootCommit, 0); + if (maxDepth > 15) { + // issue warning + events.trigger('issueWarning', + 'Max Depth Exceeded! Visuals may degrade here. ' + + 'Please start fresh or use reset to reduce the max depth' + ); + } var depthIncrement = this.getDepthIncrement(maxDepth); _.each(this.visNodeMap, function(visNode) { @@ -279,6 +293,8 @@ GitVisuals.prototype.drawTreeFirstTime = function() { this.visBranchCollection.each(function(visBranch) { visBranch.genGraphics(paper); }, this); + + this.refreshTree(); };