diff --git a/build/bundle.js b/build/bundle.js
new file mode 100644
index 00000000..443b9be3
--- /dev/null
+++ b/build/bundle.js
@@ -0,0 +1,965 @@
+(function(){var require = function (file, cwd) {
+ var resolved = require.resolve(file, cwd || '/');
+ var mod = require.modules[resolved];
+ if (!mod) throw new Error(
+ 'Failed to resolve module ' + file + ', tried ' + resolved
+ );
+ var cached = require.cache[resolved];
+ var res = cached? cached.exports : mod();
+ return res;
+};
+
+require.paths = [];
+require.modules = {};
+require.cache = {};
+require.extensions = [".js",".coffee",".json"];
+
+require._core = {
+ 'assert': true,
+ 'events': true,
+ 'fs': true,
+ 'path': true,
+ 'vm': true
+};
+
+require.resolve = (function () {
+ return function (x, cwd) {
+ if (!cwd) cwd = '/';
+
+ if (require._core[x]) return x;
+ var path = require.modules.path();
+ cwd = path.resolve('/', cwd);
+ var y = cwd || '/';
+
+ if (x.match(/^(?:\.\.?\/|\/)/)) {
+ var m = loadAsFileSync(path.resolve(y, x))
+ || loadAsDirectorySync(path.resolve(y, x));
+ if (m) return m;
+ }
+
+ var n = loadNodeModulesSync(x, y);
+ if (n) return n;
+
+ throw new Error("Cannot find module '" + x + "'");
+
+ function loadAsFileSync (x) {
+ x = path.normalize(x);
+ if (require.modules[x]) {
+ return x;
+ }
+
+ for (var i = 0; i < require.extensions.length; i++) {
+ var ext = require.extensions[i];
+ if (require.modules[x + ext]) return x + ext;
+ }
+ }
+
+ function loadAsDirectorySync (x) {
+ x = x.replace(/\/+$/, '');
+ var pkgfile = path.normalize(x + '/package.json');
+ if (require.modules[pkgfile]) {
+ var pkg = require.modules[pkgfile]();
+ var b = pkg.browserify;
+ if (typeof b === 'object' && b.main) {
+ var m = loadAsFileSync(path.resolve(x, b.main));
+ if (m) return m;
+ }
+ else if (typeof b === 'string') {
+ var m = loadAsFileSync(path.resolve(x, b));
+ if (m) return m;
+ }
+ else if (pkg.main) {
+ var m = loadAsFileSync(path.resolve(x, pkg.main));
+ if (m) return m;
+ }
+ }
+
+ return loadAsFileSync(x + '/index');
+ }
+
+ function loadNodeModulesSync (x, start) {
+ var dirs = nodeModulesPathsSync(start);
+ for (var i = 0; i < dirs.length; i++) {
+ var dir = dirs[i];
+ var m = loadAsFileSync(dir + '/' + x);
+ if (m) return m;
+ var n = loadAsDirectorySync(dir + '/' + x);
+ if (n) return n;
+ }
+
+ var m = loadAsFileSync(x);
+ if (m) return m;
+ }
+
+ function nodeModulesPathsSync (start) {
+ var parts;
+ if (start === '/') parts = [ '' ];
+ else parts = path.normalize(start).split('/');
+
+ var dirs = [];
+ for (var i = parts.length - 1; i >= 0; i--) {
+ if (parts[i] === 'node_modules') continue;
+ var dir = parts.slice(0, i + 1).join('/') + '/node_modules';
+ dirs.push(dir);
+ }
+
+ return dirs;
+ }
+ };
+})();
+
+require.alias = function (from, to) {
+ var path = require.modules.path();
+ var res = null;
+ try {
+ res = require.resolve(from + '/package.json', '/');
+ }
+ catch (err) {
+ res = require.resolve(from, '/');
+ }
+ var basedir = path.dirname(res);
+
+ var keys = (Object.keys || function (obj) {
+ var res = [];
+ for (var key in obj) res.push(key);
+ return res;
+ })(require.modules);
+
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ if (key.slice(0, basedir.length + 1) === basedir + '/') {
+ var f = key.slice(basedir.length);
+ require.modules[to + f] = require.modules[basedir + f];
+ }
+ else if (key === basedir) {
+ require.modules[to] = require.modules[basedir];
+ }
+ }
+};
+
+(function () {
+ var process = {};
+ var global = typeof window !== 'undefined' ? window : {};
+ var definedProcess = false;
+
+ require.define = function (filename, fn) {
+ if (!definedProcess && require.modules.__browserify_process) {
+ process = require.modules.__browserify_process();
+ definedProcess = true;
+ }
+
+ var dirname = require._core[filename]
+ ? ''
+ : require.modules.path().dirname(filename)
+ ;
+
+ var require_ = function (file) {
+ var requiredModule = require(file, dirname);
+ var cached = require.cache[require.resolve(file, dirname)];
+
+ if (cached && cached.parent === null) {
+ cached.parent = module_;
+ }
+
+ return requiredModule;
+ };
+ require_.resolve = function (name) {
+ return require.resolve(name, dirname);
+ };
+ require_.modules = require.modules;
+ require_.define = require.define;
+ require_.cache = require.cache;
+ var module_ = {
+ id : filename,
+ filename: filename,
+ exports : {},
+ loaded : false,
+ parent: null
+ };
+
+ require.modules[filename] = function () {
+ require.cache[filename] = module_;
+ fn.call(
+ module_.exports,
+ require_,
+ module_,
+ module_.exports,
+ dirname,
+ filename,
+ process,
+ global
+ );
+ module_.loaded = true;
+ return module_.exports;
+ };
+ };
+})();
+
+
+require.define("path",function(require,module,exports,__dirname,__filename,process,global){function filter (xs, fn) {
+ var res = [];
+ for (var i = 0; i < xs.length; i++) {
+ if (fn(xs[i], i, xs)) res.push(xs[i]);
+ }
+ return res;
+}
+
+// resolves . and .. elements in a path array with directory names there
+// must be no slashes, empty elements, or device names (c:\) in the array
+// (so also no leading and trailing slashes - it does not distinguish
+// relative and absolute paths)
+function normalizeArray(parts, allowAboveRoot) {
+ // if the path tries to go above the root, `up` ends up > 0
+ var up = 0;
+ for (var i = parts.length; i >= 0; i--) {
+ var last = parts[i];
+ if (last == '.') {
+ parts.splice(i, 1);
+ } else if (last === '..') {
+ parts.splice(i, 1);
+ up++;
+ } else if (up) {
+ parts.splice(i, 1);
+ up--;
+ }
+ }
+
+ // if the path is allowed to go above the root, restore leading ..s
+ if (allowAboveRoot) {
+ for (; up--; up) {
+ parts.unshift('..');
+ }
+ }
+
+ return parts;
+}
+
+// Regex to split a filename into [*, dir, basename, ext]
+// posix version
+var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/;
+
+// path.resolve([from ...], to)
+// posix version
+exports.resolve = function() {
+var resolvedPath = '',
+ resolvedAbsolute = false;
+
+for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {
+ var path = (i >= 0)
+ ? arguments[i]
+ : process.cwd();
+
+ // Skip empty and invalid entries
+ if (typeof path !== 'string' || !path) {
+ continue;
+ }
+
+ resolvedPath = path + '/' + resolvedPath;
+ resolvedAbsolute = path.charAt(0) === '/';
+}
+
+// At this point the path should be resolved to a full absolute path, but
+// handle relative paths to be safe (might happen when process.cwd() fails)
+
+// Normalize the path
+resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
+ return !!p;
+ }), !resolvedAbsolute).join('/');
+
+ return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
+};
+
+// path.normalize(path)
+// posix version
+exports.normalize = function(path) {
+var isAbsolute = path.charAt(0) === '/',
+ trailingSlash = path.slice(-1) === '/';
+
+// Normalize the path
+path = normalizeArray(filter(path.split('/'), function(p) {
+ return !!p;
+ }), !isAbsolute).join('/');
+
+ if (!path && !isAbsolute) {
+ path = '.';
+ }
+ if (path && trailingSlash) {
+ path += '/';
+ }
+
+ return (isAbsolute ? '/' : '') + path;
+};
+
+
+// posix version
+exports.join = function() {
+ var paths = Array.prototype.slice.call(arguments, 0);
+ return exports.normalize(filter(paths, function(p, index) {
+ return p && typeof p === 'string';
+ }).join('/'));
+};
+
+
+exports.dirname = function(path) {
+ var dir = splitPathRe.exec(path)[1] || '';
+ var isWindows = false;
+ if (!dir) {
+ // No dirname
+ return '.';
+ } else if (dir.length === 1 ||
+ (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {
+ // It is just a slash or a drive letter with a slash
+ return dir;
+ } else {
+ // It is a full dirname, strip trailing slash
+ return dir.substring(0, dir.length - 1);
+ }
+};
+
+
+exports.basename = function(path, ext) {
+ var f = splitPathRe.exec(path)[2] || '';
+ // TODO: make this comparison case-insensitive on windows?
+ if (ext && f.substr(-1 * ext.length) === ext) {
+ f = f.substr(0, f.length - ext.length);
+ }
+ return f;
+};
+
+
+exports.extname = function(path) {
+ return splitPathRe.exec(path)[3] || '';
+};
+
+});
+
+require.define("__browserify_process",function(require,module,exports,__dirname,__filename,process,global){var process = module.exports = {};
+
+process.nextTick = (function () {
+ var canSetImmediate = typeof window !== 'undefined'
+ && window.setImmediate;
+ var canPost = typeof window !== 'undefined'
+ && window.postMessage && window.addEventListener
+ ;
+
+ if (canSetImmediate) {
+ return function (f) { return window.setImmediate(f) };
+ }
+
+ if (canPost) {
+ var queue = [];
+ window.addEventListener('message', function (ev) {
+ if (ev.source === window && ev.data === 'browserify-tick') {
+ ev.stopPropagation();
+ if (queue.length > 0) {
+ var fn = queue.shift();
+ fn();
+ }
+ }
+ }, true);
+
+ return function nextTick(fn) {
+ queue.push(fn);
+ window.postMessage('browserify-tick', '*');
+ };
+ }
+
+ return function nextTick(fn) {
+ setTimeout(fn, 0);
+ };
+})();
+
+process.title = 'browser';
+process.browser = true;
+process.env = {};
+process.argv = [];
+
+process.binding = function (name) {
+ if (name === 'evals') return (require)('vm')
+ else throw new Error('No such module. (Possibly not yet loaded)')
+};
+
+(function () {
+ var cwd = '/';
+ var path;
+ process.cwd = function () { return cwd };
+ process.chdir = function (dir) {
+ if (!path) path = require('path');
+ cwd = path.resolve(dir, cwd);
+ };
+})();
+
+});
+
+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.
+ */
+
+// essentially a static class
+function AnimationFactory() {
+
+}
+
+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.define("/main.js",function(require,module,exports,__dirname,__filename,process,global){var AnimationFactory = require('./animationFactory').AnimationFactory;
+
+/**
+ * Globals
+ */
+var events = _.clone(Backbone.Events);
+var ui = null;
+var animationFactory = null;
+
+/**
+ * Static Classes
+ */
+animationFactory = new AnimationFactory();
+
+///////////////////////////////////////////////////////////////////////
+
+$(document).ready(function(){
+ ui = new UI();
+ mainVis = new 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() {
+ // static classes
+ this.commandCollection = new CommandCollection();
+
+ this.commandBuffer = new CommandBuffer({
+ collection: this.commandCollection
+ });
+
+ this.commandPromptView = new CommandPromptView({
+ el: $('#commandLineBar'),
+ collection: this.commandCollection
+ });
+ this.commandLineHistoryView = new CommandLineHistoryView({
+ el: $('#commandLineHistory'),
+ collection: this.commandCollection
+ });
+
+ $('#commandTextField').focus();
+}
+
+exports.events = events;
+exports.ui = ui;
+exports.animationFactory = animationFactory;
+
+
+});
+require("/main.js");
+
+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.
+ */
+
+// essentially a static class
+function AnimationFactory() {
+
+}
+
+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");
+})();
diff --git a/src/animationFactory.js b/src/animationFactory.js
index d420a2b0..bc6156c1 100644
--- a/src/animationFactory.js
+++ b/src/animationFactory.js
@@ -249,3 +249,6 @@ AnimationFactory.prototype.genFromToSnapshotAnimation = function(
};
};
+exports.AnimationFactory = AnimationFactory;
+
+
diff --git a/src/index.html b/src/index.html
index 13bf2313..fb67e28a 100644
--- a/src/index.html
+++ b/src/index.html
@@ -145,12 +145,18 @@
-
+
-
+
+
+
+
+