diff --git a/build/bundle.js b/build/bundle.js index 9d8e6a93..245323e5 100644 --- a/build/bundle.js +++ b/build/bundle.js @@ -392,8 +392,8 @@ process.binding = function (name) { }); -require.define("/tree.js",function(require,module,exports,__dirname,__filename,process,global){var Main = require('./app/main'); -var GRAPHICS = require('./constants').GRAPHICS; +require.define("/tree/index.js",function(require,module,exports,__dirname,__filename,process,global){var Main = require('../app/main'); +var GRAPHICS = require('../constants').GRAPHICS; var randomHueString = function() { var hue = Math.random(); @@ -962,7 +962,7 @@ var VisNode = VisBase.extend({ this.get('text').attr(attr.text); return; } - + var s = speed !== undefined ? speed : this.get('animationSpeed'); var e = easing || this.get('animationEasing'); @@ -1411,15 +1411,15 @@ exports.getUI = function() { }); -require.define("/visuals.js",function(require,module,exports,__dirname,__filename,process,global){var Main = require('./app/main'); -var GRAPHICS = require('./constants').GRAPHICS; -var GLOBAL = require('./constants').GLOBAL; +require.define("/visuals/index.js",function(require,module,exports,__dirname,__filename,process,global){var Main = require('../app/main'); +var GRAPHICS = require('../constants').GRAPHICS; +var GLOBAL = require('../constants').GLOBAL; -var Collections = require('./collections'); +var Collections = require('../collections'); var CommitCollection = Collections.CommitCollection; var BranchCollection = Collections.BranchCollection; -var Tree = require('./tree'); +var Tree = require('../tree'); var VisEdgeCollection = Tree.VisEdgeCollection; var VisBranchCollection = Tree.VisBranchCollection; var VisNode = Tree.VisNode; @@ -1450,7 +1450,7 @@ var Visualization = Backbone.View.extend({ paper: this.paper }); - var GitEngine = require('./git').GitEngine; + var GitEngine = require('../git').GitEngine; this.gitEngine = new GitEngine({ collection: this.commitCollection, branches: this.branchCollection, @@ -2093,7 +2093,7 @@ exports.Visualization = Visualization; }); -require.define("/constants.js",function(require,module,exports,__dirname,__filename,process,global){/** +require.define("/constants/index.js",function(require,module,exports,__dirname,__filename,process,global){/** * Constants....!!! */ var TIME = { @@ -5306,56 +5306,6 @@ exports.LevelEngine = LevelEngine; }); -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("/constants.js"); - require.define("/debug.js",function(require,module,exports,__dirname,__filename,process,global){var toGlobalize = { Tree: require('./tree'), Visuals: require('./visuals'), @@ -5472,1651 +5422,4 @@ exports.LevelEngine = LevelEngine; }); require("/levels.js"); -require.define("/tree.js",function(require,module,exports,__dirname,__filename,process,global){var Main = require('./app/main'); -var GRAPHICS = require('./constants').GRAPHICS; - -var randomHueString = function() { - var hue = Math.random(); - var str = 'hsb(' + String(hue) + ',0.7,1)'; - return str; -}; - -var VisBase = Backbone.Model.extend({ - removeKeys: function(keys) { - _.each(keys, function(key) { - if (this.get(key)) { - this.get(key).remove(); - } - }, this); - } -}); - -var VisBranch = VisBase.extend({ - defaults: { - pos: null, - text: null, - rect: null, - arrow: null, - isHead: false, - flip: 1, - - fill: GRAPHICS.rectFill, - stroke: GRAPHICS.rectStroke, - 'stroke-width': GRAPHICS.rectStrokeWidth, - - offsetX: GRAPHICS.nodeRadius * 4.75, - offsetY: 0, - arrowHeight: 14, - arrowInnerSkew: 0, - arrowEdgeHeight: 6, - arrowLength: 14, - arrowOffsetFromCircleX: 10, - - vPad: 5, - hPad: 5, - - animationSpeed: GRAPHICS.defaultAnimationTime, - animationEasing: GRAPHICS.defaultEasing - }, - - validateAtInit: function() { - if (!this.get('branch')) { - throw new Error('need a branch!'); - } - }, - - getID: function() { - return this.get('branch').get('id'); - }, - - initialize: function() { - this.validateAtInit(); - - // shorthand notation for the main objects - this.gitVisuals = this.get('gitVisuals'); - this.gitEngine = this.get('gitEngine'); - if (!this.gitEngine) { - console.log('throw damnit'); - throw new Error('asd'); - } - - this.get('branch').set('visBranch', this); - var id = this.get('branch').get('id'); - - if (id == 'HEAD') { - // switch to a head ref - this.set('isHead', true); - this.set('flip', -1); - - this.set('fill', GRAPHICS.headRectFill); - } else if (id !== 'master') { - // we need to set our color to something random - this.set('fill', randomHueString()); - } - }, - - getCommitPosition: function() { - var commit = this.gitEngine.getCommitFromRef(this.get('branch')); - var visNode = commit.get('visNode'); - return visNode.getScreenCoords(); - }, - - getBranchStackIndex: function() { - if (this.get('isHead')) { - // head is never stacked with other branches - return 0; - } - - var myArray = this.getBranchStackArray(); - var index = -1; - _.each(myArray, function(branch, i) { - if (branch.obj == this.get('branch')) { - index = i; - } - }, this); - return index; - }, - - getBranchStackLength: function() { - if (this.get('isHead')) { - // head is always by itself - return 1; - } - - return this.getBranchStackArray().length; - }, - - getBranchStackArray: function() { - var arr = this.gitVisuals.branchStackMap[this.get('branch').get('target').get('id')]; - if (arr === undefined) { - // this only occurs when we are generating graphics inside of - // a new Branch instantiation, so we need to force the update - this.gitVisuals.calcBranchStacks(); - return this.getBranchStackArray(); - } - return arr; - }, - - getTextPosition: function() { - var pos = this.getCommitPosition(); - - // then order yourself accordingly. we use alphabetical sorting - // so everything is independent - var myPos = this.getBranchStackIndex(); - return { - x: pos.x + this.get('flip') * this.get('offsetX'), - y: pos.y + myPos * GRAPHICS.multiBranchY + this.get('offsetY') - }; - }, - - getRectPosition: function() { - var pos = this.getTextPosition(); - var f = this.get('flip'); - - // first get text width and height - var textSize = this.getTextSize(); - return { - x: pos.x - 0.5 * textSize.w - this.get('hPad'), - y: pos.y - 0.5 * textSize.h - this.get('vPad') - }; - }, - - getArrowPath: function() { - // should make these util functions... - var offset2d = function(pos, x, y) { - return { - x: pos.x + x, - y: pos.y + y - }; - }; - var toStringCoords = function(pos) { - return String(Math.round(pos.x)) + ',' + String(Math.round(pos.y)); - }; - var f = this.get('flip'); - - var arrowTip = offset2d(this.getCommitPosition(), - f * this.get('arrowOffsetFromCircleX'), - 0 - ); - var arrowEdgeUp = offset2d(arrowTip, f * this.get('arrowLength'), -this.get('arrowHeight')); - var arrowEdgeLow = offset2d(arrowTip, f * this.get('arrowLength'), this.get('arrowHeight')); - - var arrowInnerUp = offset2d(arrowEdgeUp, - f * this.get('arrowInnerSkew'), - this.get('arrowEdgeHeight') - ); - var arrowInnerLow = offset2d(arrowEdgeLow, - f * this.get('arrowInnerSkew'), - -this.get('arrowEdgeHeight') - ); - - var tailLength = 49; - var arrowStartUp = offset2d(arrowInnerUp, f * tailLength, 0); - var arrowStartLow = offset2d(arrowInnerLow, f * tailLength, 0); - - var pathStr = ''; - pathStr += 'M' + toStringCoords(arrowStartUp) + ' '; - var coords = [ - arrowInnerUp, - arrowEdgeUp, - arrowTip, - arrowEdgeLow, - arrowInnerLow, - arrowStartLow - ]; - _.each(coords, function(pos) { - pathStr += 'L' + toStringCoords(pos) + ' '; - }, this); - pathStr += 'z'; - return pathStr; - }, - - getTextSize: function() { - var getTextWidth = function(visBranch) { - var textNode = visBranch.get('text').node; - return (textNode === null) ? 1 : textNode.clientWidth; - }; - - var textNode = this.get('text').node; - if (this.get('isHead')) { - // HEAD is a special case - return { - w: textNode.clientWidth, - h: textNode.clientHeight - }; - } - - var maxWidth = 0; - _.each(this.getBranchStackArray(), function(branch) { - maxWidth = Math.max(maxWidth, getTextWidth( - branch.obj.get('visBranch') - )); - }); - - return { - w: maxWidth, - h: textNode.clientHeight - }; - }, - - getSingleRectSize: function() { - var textSize = this.getTextSize(); - var vPad = this.get('vPad'); - var hPad = this.get('hPad'); - return { - w: textSize.w + vPad * 2, - h: textSize.h + hPad * 2 - }; - }, - - getRectSize: function() { - var textSize = this.getTextSize(); - // enforce padding - var vPad = this.get('vPad'); - var hPad = this.get('hPad'); - - // number of other branch names we are housing - var totalNum = this.getBranchStackLength(); - return { - w: textSize.w + vPad * 2, - h: textSize.h * totalNum * 1.1 + hPad * 2 - }; - }, - - getName: function() { - var name = this.get('branch').get('id'); - var selected = this.gitEngine.HEAD.get('target').get('id'); - - var add = (selected == name) ? '*' : ''; - return name + add; - }, - - nonTextToFront: function() { - this.get('arrow').toFront(); - this.get('rect').toFront(); - }, - - textToFront: function() { - this.get('text').toFront(); - }, - - getFill: function() { - // in the easy case, just return your own fill if you are: - // - the HEAD ref - // - by yourself (length of 1) - // - part of a multi branch, but your thing is hidden - if (this.get('isHead') || - this.getBranchStackLength() == 1 || - this.getBranchStackIndex() !== 0) { - return this.get('fill'); - } - - // woof. now it's hard, we need to blend hues... - return this.gitVisuals.blendHuesFromBranchStack(this.getBranchStackArray()); - }, - - remove: function() { - this.removeKeys(['text', 'arrow', 'rect']); - // also need to remove from this.gitVisuals - this.gitVisuals.removeVisBranch(this); - }, - - genGraphics: function(paper) { - var textPos = this.getTextPosition(); - var name = this.getName(); - var text; - - // when from a reload, we dont need to generate the text - text = paper.text(textPos.x, textPos.y, String(name)); - text.attr({ - 'font-size': 14, - 'font-family': 'Monaco, Courier, font-monospace', - opacity: this.getTextOpacity() - }); - this.set('text', text); - - var rectPos = this.getRectPosition(); - var sizeOfRect = this.getRectSize(); - var rect = paper - .rect(rectPos.x, rectPos.y, sizeOfRect.w, sizeOfRect.h, 8) - .attr(this.getAttributes().rect); - this.set('rect', rect); - - var arrowPath = this.getArrowPath(); - var arrow = paper - .path(arrowPath) - .attr(this.getAttributes().arrow); - this.set('arrow', arrow); - - rect.toFront(); - text.toFront(); - }, - - updateName: function() { - this.get('text').attr({ - text: this.getName() - }); - }, - - getNonTextOpacity: function() { - if (this.get('isHead')) { - return this.gitEngine.getDetachedHead() ? 1 : 0; - } - return this.getBranchStackIndex() === 0 ? 1 : 0.0; - }, - - getTextOpacity: function() { - if (this.get('isHead')) { - return this.gitEngine.getDetachedHead() ? 1 : 0; - } - return 1; - }, - - getAttributes: function() { - var nonTextOpacity = this.getNonTextOpacity(); - var textOpacity = this.getTextOpacity(); - this.updateName(); - - var textPos = this.getTextPosition(); - var rectPos = this.getRectPosition(); - var rectSize = this.getRectSize(); - - var arrowPath = this.getArrowPath(); - - return { - text: { - x: textPos.x, - y: textPos.y, - opacity: textOpacity - }, - rect: { - x: rectPos.x, - y: rectPos.y, - width: rectSize.w, - height: rectSize.h, - opacity: nonTextOpacity, - fill: this.getFill(), - stroke: this.get('stroke'), - 'stroke-width': this.get('stroke-width') - }, - arrow: { - path: arrowPath, - opacity: nonTextOpacity, - fill: this.getFill(), - stroke: this.get('stroke'), - 'stroke-width': this.get('stroke-width') - } - }; - }, - - animateUpdatedPos: function(speed, easing) { - var attr = this.getAttributes(); - this.animateToAttr(attr, speed, easing); - }, - - animateFromAttrToAttr: function(fromAttr, toAttr, speed, easing) { - // an animation of 0 is essentially setting the attribute directly - this.animateToAttr(fromAttr, 0); - this.animateToAttr(toAttr, speed, easing); - }, - - animateToAttr: function(attr, speed, easing) { - if (speed === 0) { - this.get('text').attr(attr.text); - this.get('rect').attr(attr.rect); - this.get('arrow').attr(attr.arrow); - return; - } - - var s = speed !== undefined ? speed : this.get('animationSpeed'); - var e = easing || this.get('animationEasing'); - - this.get('text').stop().animate(attr.text, s, e); - this.get('rect').stop().animate(attr.rect, s, e); - this.get('arrow').stop().animate(attr.arrow, s, e); - } -}); - - -var VisNode = VisBase.extend({ - defaults: { - depth: undefined, - maxWidth: null, - outgoingEdges: null, - - circle: null, - text: null, - - id: null, - pos: null, - radius: null, - - commit: null, - animationSpeed: GRAPHICS.defaultAnimationTime, - animationEasing: GRAPHICS.defaultEasing, - - fill: GRAPHICS.defaultNodeFill, - 'stroke-width': GRAPHICS.defaultNodeStrokeWidth, - stroke: GRAPHICS.defaultNodeStroke - }, - - getID: function() { - return this.get('id'); - }, - - validateAtInit: function() { - if (!this.get('id')) { - throw new Error('need id for mapping'); - } - if (!this.get('commit')) { - throw new Error('need commit for linking'); - } - - if (!this.get('pos')) { - this.set('pos', { - x: Math.random(), - y: Math.random() - }); - } - }, - - initialize: function() { - this.validateAtInit(); - // shorthand for the main objects - this.gitVisuals = this.get('gitVisuals'); - this.gitEngine = this.get('gitEngine'); - - this.set('outgoingEdges', []); - }, - - setDepth: function(depth) { - // for merge commits we need to max the depths across all - this.set('depth', Math.max(this.get('depth') || 0, depth)); - }, - - setDepthBasedOn: function(depthIncrement) { - if (this.get('depth') === undefined) { - debugger; - throw new Error('no depth yet!'); - } - var pos = this.get('pos'); - pos.y = this.get('depth') * depthIncrement; - }, - - getMaxWidthScaled: function() { - // returns our max width scaled based on if we are visible - // from a branch or not - var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('commit')); - var map = { - branch: 1, - head: 0.3, - none: 0.1 - }; - if (map[stat] === undefined) { throw new Error('bad stat'); } - return map[stat] * this.get('maxWidth'); - }, - - toFront: function() { - this.get('circle').toFront(); - this.get('text').toFront(); - }, - - getOpacity: function() { - var map = { - 'branch': 1, - 'head': GRAPHICS.upstreamHeadOpacity, - 'none': GRAPHICS.upstreamNoneOpacity - }; - - var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('commit')); - if (map[stat] === undefined) { - throw new Error('invalid status'); - } - return map[stat]; - }, - - getTextScreenCoords: function() { - return this.getScreenCoords(); - }, - - getAttributes: function() { - var pos = this.getScreenCoords(); - var textPos = this.getTextScreenCoords(); - var opacity = this.getOpacity(); - - return { - circle: { - cx: pos.x, - cy: pos.y, - opacity: opacity, - r: this.getRadius(), - fill: this.getFill(), - 'stroke-width': this.get('stroke-width'), - stroke: this.get('stroke') - }, - text: { - x: textPos.x, - y: textPos.y, - opacity: opacity - } - }; - }, - - highlightTo: function(visObj, speed, easing) { - // a small function to highlight the color of a node for demonstration purposes - var color = visObj.get('fill'); - - var attr = { - circle: { - fill: color, - stroke: color, - 'stroke-width': this.get('stroke-width') * 5 - }, - text: {} - }; - - this.animateToAttr(attr, speed, easing); - }, - - animateUpdatedPosition: function(speed, easing) { - var attr = this.getAttributes(); - this.animateToAttr(attr, speed, easing); - }, - - animateFromAttrToAttr: function(fromAttr, toAttr, speed, easing) { - // an animation of 0 is essentially setting the attribute directly - this.animateToAttr(fromAttr, 0); - this.animateToAttr(toAttr, speed, easing); - }, - - animateToSnapshot: function(snapShot, speed, easing) { - if (!snapShot[this.getID()]) { - return; - } - this.animateToAttr(snapShot[this.getID()], speed, easing); - }, - - animateToAttr: function(attr, speed, easing) { - if (speed === 0) { - this.get('circle').attr(attr.circle); - this.get('text').attr(attr.text); - return; - } - - var s = speed !== undefined ? speed : this.get('animationSpeed'); - var e = easing || this.get('animationEasing'); - - this.get('circle').stop().animate(attr.circle, s, e); - this.get('text').stop().animate(attr.text, s, e); - - // animate the x attribute without bouncing so it looks like there's - // gravity in only one direction. Just a small animation polish - this.get('circle').animate(attr.circle.cx, s, 'easeInOut'); - this.get('text').animate(attr.text.x, s, 'easeInOut'); - }, - - getScreenCoords: function() { - var pos = this.get('pos'); - return this.gitVisuals.toScreenCoords(pos); - }, - - getRadius: function() { - return this.get('radius') || GRAPHICS.nodeRadius; - }, - - getParentScreenCoords: function() { - return this.get('commit').get('parents')[0].get('visNode').getScreenCoords(); - }, - - setBirthPosition: function() { - // utility method for animating it out from underneath a parent - var parentCoords = this.getParentScreenCoords(); - - this.get('circle').attr({ - cx: parentCoords.x, - cy: parentCoords.y, - opacity: 0, - r: 0 - }); - this.get('text').attr({ - x: parentCoords.x, - y: parentCoords.y, - opacity: 0 - }); - }, - - setBirthFromSnapshot: function(beforeSnapshot) { - // first get parent attribute - // woof bad data access. TODO - var parentID = this.get('commit').get('parents')[0].get('visNode').getID(); - var parentAttr = beforeSnapshot[parentID]; - - // then set myself faded on top of parent - this.get('circle').attr({ - opacity: 0, - r: 0, - cx: parentAttr.circle.cx, - cy: parentAttr.circle.cy - }); - - this.get('text').attr({ - opacity: 0, - x: parentAttr.text.x, - y: parentAttr.text.y - }); - - // then do edges - var parentCoords = { - x: parentAttr.circle.cx, - y: parentAttr.circle.cy - }; - this.setOutgoingEdgesBirthPosition(parentCoords); - }, - - setBirth: function() { - this.setBirthPosition(); - this.setOutgoingEdgesBirthPosition(this.getParentScreenCoords()); - }, - - setOutgoingEdgesOpacity: function(opacity) { - _.each(this.get('outgoingEdges'), function(edge) { - edge.setOpacity(opacity); - }); - }, - - animateOutgoingEdgesToAttr: function(snapShot, speed, easing) { - _.each(this.get('outgoingEdges'), function(edge) { - var attr = snapShot[edge.getID()]; - edge.animateToAttr(attr); - }, this); - }, - - animateOutgoingEdges: function(speed, easing) { - _.each(this.get('outgoingEdges'), function(edge) { - edge.animateUpdatedPath(speed, easing); - }, this); - }, - - animateOutgoingEdgesFromSnapshot: function(snapshot, speed, easing) { - _.each(this.get('outgoingEdges'), function(edge) { - var attr = snapshot[edge.getID()]; - edge.animateToAttr(attr, speed, easing); - }, this); - }, - - setOutgoingEdgesBirthPosition: function(parentCoords) { - - _.each(this.get('outgoingEdges'), function(edge) { - var headPos = edge.get('head').getScreenCoords(); - var path = edge.genSmoothBezierPathStringFromCoords(parentCoords, headPos); - edge.get('path').stop().attr({ - path: path, - opacity: 0 - }); - }, this); - }, - - parentInFront: function() { - // woof! talk about bad data access - this.get('commit').get('parents')[0].get('visNode').toFront(); - }, - - getFontSize: function(str) { - if (str.length < 3) { - return 12; - } else if (str.length < 5) { - return 10; - } else { - return 8; - } - }, - - getFill: function() { - // first get our status, might be easy from this - var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('commit')); - if (stat == 'head') { - return GRAPHICS.headRectFill; - } else if (stat == 'none') { - return GRAPHICS.orphanNodeFill; - } - - // now we need to get branch hues - return this.gitVisuals.getBlendedHuesForCommit(this.get('commit')); - }, - - attachClickHandlers: function() { - var commandStr = 'git show ' + this.get('commit').get('id'); - _.each([this.get('circle'), this.get('text')], function(rObj) { - rObj.click(function() { - Main.getEvents().trigger('processCommandFromEvent', commandStr); - }); - }); - }, - - setOpacity: function(opacity) { - opacity = (opacity === undefined) ? 1 : opacity; - - // set the opacity on my stuff - var keys = ['circle', 'text']; - _.each(keys, function(key) { - this.get(key).attr({ - opacity: opacity - }); - }, this); - }, - - remove: function() { - this.removeKeys(['circle'], ['text']); - // needs a manual removal of text for whatever reason - this.get('text').remove(); - - this.gitVisuals.removeVisNode(this); - }, - - removeAll: function() { - this.remove(); - _.each(this.get('outgoingEdges'), function(edge) { - edge.remove(); - }, this); - }, - - genGraphics: function() { - var paper = this.gitVisuals.paper; - - var pos = this.getScreenCoords(); - var textPos = this.getTextScreenCoords(); - - var circle = paper.circle( - pos.x, - pos.y, - this.getRadius() - ).attr(this.getAttributes().circle); - - var text = paper.text(textPos.x, textPos.y, String(this.get('id'))); - text.attr({ - 'font-size': this.getFontSize(this.get('id')), - 'font-weight': 'bold', - 'font-family': 'Monaco, Courier, font-monospace', - opacity: this.getOpacity() - }); - - this.set('circle', circle); - this.set('text', text); - - this.attachClickHandlers(); - } -}); - -var VisEdge = VisBase.extend({ - defaults: { - tail: null, - head: null, - animationSpeed: GRAPHICS.defaultAnimationTime, - animationEasing: GRAPHICS.defaultEasing - }, - - validateAtInit: function() { - var required = ['tail', 'head']; - _.each(required, function(key) { - if (!this.get(key)) { - throw new Error(key + ' is required!'); - } - }, this); - }, - - getID: function() { - return this.get('tail').get('id') + '.' + this.get('head').get('id'); - }, - - initialize: function() { - this.validateAtInit(); - - // shorthand for the main objects - this.gitVisuals = this.get('gitVisuals'); - this.gitEngine = this.get('gitEngine'); - - this.get('tail').get('outgoingEdges').push(this); - }, - - remove: function() { - this.removeKeys(['path']); - this.gitVisuals.removeVisEdge(this); - }, - - genSmoothBezierPathString: function(tail, head) { - var tailPos = tail.getScreenCoords(); - var headPos = head.getScreenCoords(); - return this.genSmoothBezierPathStringFromCoords(tailPos, headPos); - }, - - genSmoothBezierPathStringFromCoords: function(tailPos, headPos) { - // we need to generate the path and control points for the bezier. format - // is M(move abs) C (curve to) (control point 1) (control point 2) (final point) - // the control points have to be __below__ to get the curve starting off straight. - - var coords = function(pos) { - return String(Math.round(pos.x)) + ',' + String(Math.round(pos.y)); - }; - var offset = function(pos, dir, delta) { - delta = delta || GRAPHICS.curveControlPointOffset; - return { - x: pos.x, - y: pos.y + delta * dir - }; - }; - var offset2d = function(pos, x, y) { - return { - x: pos.x + x, - y: pos.y + y - }; - }; - - // first offset tail and head by radii - tailPos = offset(tailPos, -1, this.get('tail').getRadius()); - headPos = offset(headPos, 1, this.get('head').getRadius()); - - var str = ''; - // first move to bottom of tail - str += 'M' + coords(tailPos) + ' '; - // start bezier - str += 'C'; - // then control points above tail and below head - str += coords(offset(tailPos, -1)) + ' '; - str += coords(offset(headPos, 1)) + ' '; - // now finish - str += coords(headPos); - - // arrow head - var delta = GRAPHICS.arrowHeadSize || 10; - str += ' L' + coords(offset2d(headPos, -delta, delta)); - str += ' L' + coords(offset2d(headPos, delta, delta)); - str += ' L' + coords(headPos); - - // then go back, so we can fill correctly - str += 'C'; - str += coords(offset(headPos, 1)) + ' '; - str += coords(offset(tailPos, -1)) + ' '; - str += coords(tailPos); - - return str; - }, - - getBezierCurve: function() { - return this.genSmoothBezierPathString(this.get('tail'), this.get('head')); - }, - - getStrokeColor: function() { - return GRAPHICS.visBranchStrokeColorNone; - }, - - setOpacity: function(opacity) { - opacity = (opacity === undefined) ? 1 : opacity; - - this.get('path').attr({opacity: opacity}); - }, - - genGraphics: function(paper) { - var pathString = this.getBezierCurve(); - - var path = paper.path(pathString).attr({ - 'stroke-width': GRAPHICS.visBranchStrokeWidth, - 'stroke': this.getStrokeColor(), - 'stroke-linecap': 'round', - 'stroke-linejoin': 'round', - 'fill': this.getStrokeColor() - }); - path.toBack(); - this.set('path', path); - }, - - getOpacity: function() { - var stat = this.gitVisuals.getCommitUpstreamStatus(this.get('tail')); - var map = { - 'branch': 1, - 'head': GRAPHICS.edgeUpstreamHeadOpacity, - 'none': GRAPHICS.edgeUpstreamNoneOpacity - }; - - if (map[stat] === undefined) { throw new Error('bad stat'); } - return map[stat]; - }, - - getAttributes: function() { - var newPath = this.getBezierCurve(); - var opacity = this.getOpacity(); - return { - path: { - path: newPath, - opacity: opacity - } - }; - }, - - animateUpdatedPath: function(speed, easing) { - var attr = this.getAttributes(); - this.animateToAttr(attr, speed, easing); - }, - - animateFromAttrToAttr: function(fromAttr, toAttr, speed, easing) { - // an animation of 0 is essentially setting the attribute directly - this.animateToAttr(fromAttr, 0); - this.animateToAttr(toAttr, speed, easing); - }, - - animateToAttr: function(attr, speed, easing) { - if (speed === 0) { - this.get('path').attr(attr.path); - return; - } - - this.get('path').toBack(); - this.get('path').stop().animate( - attr.path, - speed !== undefined ? speed : this.get('animationSpeed'), - easing || this.get('animationEasing') - ); - } -}); - -var VisEdgeCollection = Backbone.Collection.extend({ - model: VisEdge -}); - -var VisBranchCollection = Backbone.Collection.extend({ - model: VisBranch -}); - -exports.VisEdgeCollection = VisEdgeCollection; -exports.VisBranchCollection = VisBranchCollection; -exports.VisNode = VisNode; -exports.VisEdge = VisEdge; -exports.VisBranch = VisBranch; - - -}); -require("/tree.js"); - -require.define("/visuals.js",function(require,module,exports,__dirname,__filename,process,global){var Main = require('./app/main'); -var GRAPHICS = require('./constants').GRAPHICS; -var GLOBAL = require('./constants').GLOBAL; - -var Collections = require('./collections'); -var CommitCollection = Collections.CommitCollection; -var BranchCollection = Collections.BranchCollection; - -var Tree = require('./tree'); -var VisEdgeCollection = Tree.VisEdgeCollection; -var VisBranchCollection = Tree.VisBranchCollection; -var VisNode = Tree.VisNode; -var VisBranch = Tree.VisBranch; -var VisEdge = Tree.VisEdge; - -var Visualization = Backbone.View.extend({ - initialize: function(options) { - var _this = this; - new Raphael(10, 10, 200, 200, function() { - - // for some reason raphael calls this function with a predefined - // context... - // so switch it - _this.paperInitialize(this); - }); - }, - - paperInitialize: function(paper, options) { - this.paper = paper; - - this.commitCollection = new CommitCollection(); - this.branchCollection = new BranchCollection(); - - this.gitVisuals = new GitVisuals({ - commitCollection: this.commitCollection, - branchCollection: this.branchCollection, - paper: this.paper - }); - - var GitEngine = require('./git').GitEngine; - this.gitEngine = new GitEngine({ - collection: this.commitCollection, - branches: this.branchCollection, - gitVisuals: this.gitVisuals - }); - this.gitEngine.init(); - this.gitVisuals.assignGitEngine(this.gitEngine); - - this.myResize(); - $(window).on('resize', _.bind(this.myResize, this)); - this.gitVisuals.drawTreeFirstTime(); - }, - - myResize: function() { - var smaller = 1; - var el = this.el; - - var left = el.offsetLeft; - var top = el.offsetTop; - var width = el.clientWidth - smaller; - var height = el.clientHeight - smaller; - - $(this.paper.canvas).css({ - left: left + 'px', - top: top + 'px' - }); - this.paper.setSize(width, height); - this.gitVisuals.canvasResize(width, height); - } - -}); - -function GitVisuals(options) { - this.commitCollection = options.commitCollection; - this.branchCollection = options.branchCollection; - this.visNodeMap = {}; - - this.visEdgeCollection = new VisEdgeCollection(); - this.visBranchCollection = new VisBranchCollection(); - this.commitMap = {}; - - this.rootCommit = null; - this.branchStackMap = null; - this.upstreamBranchSet = null; - this.upstreamHeadSet = null; - - this.paper = options.paper; - this.gitReady = false; - - this.branchCollection.on('add', this.addBranchFromEvent, this); - this.branchCollection.on('remove', this.removeBranch, this); - this.deferred = []; - - Main.getEvents().on('refreshTree', _.bind( - this.refreshTree, this - )); -} - -GitVisuals.prototype.defer = function(action) { - this.deferred.push(action); -}; - -GitVisuals.prototype.deferFlush = function() { - _.each(this.deferred, function(action) { - action(); - }, this); - this.deferred = []; -}; - -GitVisuals.prototype.resetAll = function() { - // make sure to copy these collections because we remove - // items in place and underscore is too dumb to detect length change - var edges = this.visEdgeCollection.toArray(); - _.each(edges, function(visEdge) { - visEdge.remove(); - }, this); - - var branches = this.visBranchCollection.toArray(); - _.each(branches, function(visBranch) { - visBranch.remove(); - }, this); - - _.each(this.visNodeMap, function(visNode) { - visNode.remove(); - }, this); - - this.visEdgeCollection.reset(); - this.visBranchCollection.reset(); - - this.visNodeMap = {}; - this.rootCommit = null; - this.commitMap = {}; -}; - -GitVisuals.prototype.assignGitEngine = function(gitEngine) { - this.gitEngine = gitEngine; - this.initHeadBranch(); - this.deferFlush(); -}; - -GitVisuals.prototype.initHeadBranch = function() { - // it's unfortaunte we have to do this, but the head branch - // is an edge case because it's not part of a collection so - // we can't use events to load or unload it. thus we have to call - // this ugly method which will be deleted one day - - // seed this with the HEAD pseudo-branch - this.addBranchFromEvent(this.gitEngine.HEAD); -}; - -GitVisuals.prototype.getScreenBounds = function() { - // for now we return the node radius subtracted from the walls - return { - widthPadding: GRAPHICS.nodeRadius * 1.5, - heightPadding: GRAPHICS.nodeRadius * 1.5 - }; -}; - -GitVisuals.prototype.toScreenCoords = function(pos) { - if (!this.paper.width) { - throw new Error('being called too early for screen coords'); - } - var bounds = this.getScreenBounds(); - - var shrink = function(frac, total, padding) { - return padding + frac * (total - padding * 2); - }; - - return { - x: shrink(pos.x, this.paper.width, bounds.widthPadding), - y: shrink(pos.y, this.paper.height, bounds.heightPadding) - }; -}; - -GitVisuals.prototype.animateAllFromAttrToAttr = function(fromSnapshot, toSnapshot, idsToOmit) { - var animate = function(obj) { - var id = obj.getID(); - if (_.include(idsToOmit, id)) { - return; - } - - if (!fromSnapshot[id] || !toSnapshot[id]) { - // its actually ok it doesnt exist yet - return; - } - obj.animateFromAttrToAttr(fromSnapshot[id], toSnapshot[id]); - }; - - this.visBranchCollection.each(function(visBranch) { - animate(visBranch); - }); - this.visEdgeCollection.each(function(visEdge) { - animate(visEdge); - }); - _.each(this.visNodeMap, function(visNode) { - animate(visNode); - }); -}; - -/*************************************** - == BEGIN Tree Calculation Parts == - _ __ __ _ - \\/ / \ \//_ - \ \ / __| __ - \ \___/ /_____/ / - | _______ \ - \ ( ) / \_\ - \ / - | | - | | - ____+-_=+-^ ^+-=_=__________ - -^^ I drew that :D - - **************************************/ - -GitVisuals.prototype.genSnapshot = function() { - this.fullCalc(); - - var snapshot = {}; - _.each(this.visNodeMap, function(visNode) { - snapshot[visNode.get('id')] = visNode.getAttributes(); - }, this); - - this.visBranchCollection.each(function(visBranch) { - snapshot[visBranch.getID()] = visBranch.getAttributes(); - }, this); - - this.visEdgeCollection.each(function(visEdge) { - snapshot[visEdge.getID()] = visEdge.getAttributes(); - }, this); - - return snapshot; -}; - -GitVisuals.prototype.refreshTree = function(speed) { - if (!this.gitReady) { - return; - } - - // this method can only be called after graphics are rendered - this.fullCalc(); - - this.animateAll(speed); -}; - -GitVisuals.prototype.refreshTreeHarsh = function() { - this.fullCalc(); - - this.animateAll(0); -}; - -GitVisuals.prototype.animateAll = function(speed) { - this.zIndexReflow(); - - this.animateEdges(speed); - this.animateNodePositions(speed); - this.animateRefs(speed); -}; - -GitVisuals.prototype.fullCalc = function() { - this.calcTreeCoords(); - this.calcGraphicsCoords(); -}; - -GitVisuals.prototype.calcTreeCoords = function() { - // this method can only contain things that dont rely on graphics - if (!this.rootCommit) { - throw new Error('grr, no root commit!'); - } - - this.calcUpstreamSets(); - this.calcBranchStacks(); - - this.calcDepth(); - this.calcWidth(); -}; - -GitVisuals.prototype.calcGraphicsCoords = function() { - this.visBranchCollection.each(function(visBranch) { - visBranch.updateName(); - }); -}; - -GitVisuals.prototype.calcUpstreamSets = function() { - this.upstreamBranchSet = this.gitEngine.getUpstreamBranchSet(); - this.upstreamHeadSet = this.gitEngine.getUpstreamHeadSet(); -}; - -GitVisuals.prototype.getCommitUpstreamBranches = function(commit) { - return this.branchStackMap[commit.get('id')]; -}; - -GitVisuals.prototype.getBlendedHuesForCommit = function(commit) { - var branches = this.upstreamBranchSet[commit.get('id')]; - if (!branches) { - throw new Error('that commit doesnt have upstream branches!'); - } - - return this.blendHuesFromBranchStack(branches); -}; - -GitVisuals.prototype.blendHuesFromBranchStack = function(branchStackArray) { - var hueStrings = []; - _.each(branchStackArray, function(branchWrapper) { - var fill = branchWrapper.obj.get('visBranch').get('fill'); - - if (fill.slice(0,3) !== 'hsb') { - // crap! convert - var color = Raphael.color(fill); - fill = 'hsb(' + String(color.h) + ',' + String(color.l); - fill = fill + ',' + String(color.s) + ')'; - } - - hueStrings.push(fill); - }); - - return blendHueStrings(hueStrings); -}; - -GitVisuals.prototype.getCommitUpstreamStatus = function(commit) { - if (!this.upstreamBranchSet) { - throw new Error("Can't calculate this yet!"); - } - - var id = commit.get('id'); - var branch = this.upstreamBranchSet; - var head = this.upstreamHeadSet; - - if (branch[id]) { - return 'branch'; - } else if (head[id]) { - return 'head'; - } else { - return 'none'; - } -}; - -GitVisuals.prototype.calcBranchStacks = function() { - var branches = this.gitEngine.getBranches(); - var map = {}; - _.each(branches, function(branch) { - var thisId = branch.target.get('id'); - - map[thisId] = map[thisId] || []; - map[thisId].push(branch); - map[thisId].sort(function(a, b) { - var aId = a.obj.get('id'); - var bId = b.obj.get('id'); - if (aId == 'master' || bId == 'master') { - return aId == 'master' ? -1 : 1; - } - return aId.localeCompare(bId); - }); - }); - this.branchStackMap = map; -}; - -GitVisuals.prototype.calcWidth = function() { - this.maxWidthRecursive(this.rootCommit); - - this.assignBoundsRecursive(this.rootCommit, 0, 1); -}; - -GitVisuals.prototype.maxWidthRecursive = function(commit) { - var childrenTotalWidth = 0; - _.each(commit.get('children'), function(child) { - // only include this if we are the "main" parent of - // this child - if (child.isMainParent(commit)) { - var childWidth = this.maxWidthRecursive(child); - childrenTotalWidth += childWidth; - } - }, this); - - var maxWidth = Math.max(1, childrenTotalWidth); - commit.get('visNode').set('maxWidth', maxWidth); - return maxWidth; -}; - -GitVisuals.prototype.assignBoundsRecursive = function(commit, min, max) { - // I always center myself within my bounds - var myWidthPos = (min + max) / 2.0; - commit.get('visNode').get('pos').x = myWidthPos; - - if (commit.get('children').length === 0) { - return; - } - - // i have a certain length to divide up - var myLength = max - min; - // I will divide up that length based on my children's max width in a - // basic box-flex model - var totalFlex = 0; - var children = commit.get('children'); - _.each(children, function(child) { - if (child.isMainParent(commit)) { - totalFlex += child.get('visNode').getMaxWidthScaled(); - } - }, this); - - var prevBound = min; - - // now go through and do everything - // TODO: order so the max width children are in the middle!! - _.each(children, function(child) { - if (!child.isMainParent(commit)) { - return; - } - - var flex = child.get('visNode').getMaxWidthScaled(); - var portion = (flex / totalFlex) * myLength; - var childMin = prevBound; - var childMax = childMin + portion; - this.assignBoundsRecursive(child, childMin, childMax); - prevBound = childMax; - }, this); -}; - -GitVisuals.prototype.calcDepth = function() { - var maxDepth = this.calcDepthRecursive(this.rootCommit, 0); - if (maxDepth > 15) { - // issue warning - console.warn('graphics are degrading from too many layers'); - } - - var depthIncrement = this.getDepthIncrement(maxDepth); - _.each(this.visNodeMap, function(visNode) { - visNode.setDepthBasedOn(depthIncrement); - }, this); -}; - -/*************************************** - == END Tree Calculation == - _ __ __ _ - \\/ / \ \//_ - \ \ / __| __ - \ \___/ /_____/ / - | _______ \ - \ ( ) / \_\ - \ / - | | - | | - ____+-_=+-^ ^+-=_=__________ - -^^ I drew that :D - - **************************************/ - -GitVisuals.prototype.animateNodePositions = function(speed) { - _.each(this.visNodeMap, function(visNode) { - visNode.animateUpdatedPosition(speed); - }, this); -}; - -GitVisuals.prototype.turnOnPaper = function() { - this.gitReady = false; -}; - -// does making an accessor method make it any less hacky? that is the true question -GitVisuals.prototype.turnOffPaper = function() { - this.gitReady = true; -}; - -GitVisuals.prototype.addBranchFromEvent = function(branch, collection, index) { - var action = _.bind(function() { - this.addBranch(branch); - }, this); - - if (!this.gitEngine || !this.gitReady) { - this.defer(action); - } else { - action(); - } -}; - -GitVisuals.prototype.addBranch = function(branch) { - var visBranch = new VisBranch({ - branch: branch, - gitVisuals: this, - gitEngine: this.gitEngine - }); - - this.visBranchCollection.add(visBranch); - if (this.gitReady) { - visBranch.genGraphics(this.paper); - } -}; - -GitVisuals.prototype.removeVisBranch = function(visBranch) { - this.visBranchCollection.remove(visBranch); -}; - -GitVisuals.prototype.removeVisNode = function(visNode) { - this.visNodeMap[visNode.getID()] = undefined; -}; - -GitVisuals.prototype.removeVisEdge = function(visEdge) { - this.visEdgeCollection.remove(visEdge); -}; - -GitVisuals.prototype.animateRefs = function(speed) { - this.visBranchCollection.each(function(visBranch) { - visBranch.animateUpdatedPos(speed); - }, this); -}; - -GitVisuals.prototype.animateEdges = function(speed) { - this.visEdgeCollection.each(function(edge) { - edge.animateUpdatedPath(speed); - }, this); -}; - -GitVisuals.prototype.getDepthIncrement = function(maxDepth) { - // assume there are at least 7 layers until later - maxDepth = Math.max(maxDepth, 7); - var increment = 1.0 / maxDepth; - return increment; -}; - -GitVisuals.prototype.calcDepthRecursive = function(commit, depth) { - commit.get('visNode').setDepth(depth); - - var children = commit.get('children'); - var maxDepth = depth; - _.each(children, function(child) { - var d = this.calcDepthRecursive(child, depth + 1); - maxDepth = Math.max(d, maxDepth); - }, this); - - return maxDepth; -}; - -// we debounce here so we aren't firing a resize call on every resize event -// but only after they stop -GitVisuals.prototype.canvasResize = _.debounce(function(width, height) { - // refresh when we are ready - if (GLOBAL.isAnimating) { - Main.getEvents().trigger('processCommandFromEvent', 'refresh'); - } else { - this.refreshTree(); - } -}, 200); - -GitVisuals.prototype.addNode = function(id, commit) { - this.commitMap[id] = commit; - if (commit.get('rootCommit')) { - this.rootCommit = commit; - } - - var visNode = new VisNode({ - id: id, - commit: commit, - gitVisuals: this, - gitEngine: this.gitEngine - }); - this.visNodeMap[id] = visNode; - - if (this.gitReady) { - visNode.genGraphics(this.paper); - } - return visNode; -}; - -GitVisuals.prototype.addEdge = function(idTail, idHead) { - var visNodeTail = this.visNodeMap[idTail]; - var visNodeHead = this.visNodeMap[idHead]; - - if (!visNodeTail || !visNodeHead) { - throw new Error('one of the ids in (' + idTail + - ', ' + idHead + ') does not exist'); - } - - var edge = new VisEdge({ - tail: visNodeTail, - head: visNodeHead, - gitVisuals: this, - gitEngine: this.gitEngine - }); - this.visEdgeCollection.add(edge); - - if (this.gitReady) { - edge.genGraphics(this.paper); - } -}; - -GitVisuals.prototype.collectionChanged = function() { - // TODO ? -}; - -GitVisuals.prototype.zIndexReflow = function() { - this.visNodesFront(); - this.visBranchesFront(); -}; - -GitVisuals.prototype.visNodesFront = function() { - _.each(this.visNodeMap, function(visNode) { - visNode.toFront(); - }); -}; - -GitVisuals.prototype.visBranchesFront = function() { - this.visBranchCollection.each(function(vBranch) { - vBranch.nonTextToFront(); - }); - - this.visBranchCollection.each(function(vBranch) { - vBranch.textToFront(); - }); -}; - -GitVisuals.prototype.drawTreeFromReload = function() { - this.gitReady = true; - // gen all the graphics we need - this.deferFlush(); - - this.calcTreeCoords(); -}; - -GitVisuals.prototype.drawTreeFirstTime = function() { - this.gitReady = true; - this.calcTreeCoords(); - - _.each(this.visNodeMap, function(visNode) { - visNode.genGraphics(this.paper); - }, this); - - this.visEdgeCollection.each(function(edge) { - edge.genGraphics(this.paper); - }, this); - - this.visBranchCollection.each(function(visBranch) { - visBranch.genGraphics(this.paper); - }, this); - - this.zIndexReflow(); -}; - - -/************************ - * Random util functions, some from liquidGraph - ***********************/ -function blendHueStrings(hueStrings) { - // assumes a sat of 0.7 and brightness of 1 - - var x = 0; - var y = 0; - var totalSat = 0; - var totalBright = 0; - var length = hueStrings.length; - - _.each(hueStrings, function(hueString) { - var exploded = hueString.split('(')[1]; - exploded = exploded.split(')')[0]; - exploded = exploded.split(','); - - totalSat += parseFloat(exploded[1]); - totalBright += parseFloat(exploded[2]); - var hue = parseFloat(exploded[0]); - - var angle = hue * Math.PI * 2; - x += Math.cos(angle); - y += Math.sin(angle); - }); - - x = x / length; - y = y / length; - totalSat = totalSat / length; - totalBright = totalBright / length; - - var hue = Math.atan2(y, x) / (Math.PI * 2); // could fail on 0's - if (hue < 0) { - hue = hue + 1; - } - return 'hsb(' + String(hue) + ',' + String(totalSat) + ',' + String(totalBright) + ')'; -} - -exports.Visualization = Visualization; - - -}); -require("/visuals.js"); - })(); diff --git a/src/constants.js b/src/constants/index.js similarity index 100% rename from src/constants.js rename to src/constants/index.js diff --git a/src/tree.js b/src/tree/index.js similarity index 99% rename from src/tree.js rename to src/tree/index.js index 9ad926a1..c3050134 100644 --- a/src/tree.js +++ b/src/tree/index.js @@ -1,5 +1,5 @@ -var Main = require('./app/main'); -var GRAPHICS = require('./constants').GRAPHICS; +var Main = require('../app/main'); +var GRAPHICS = require('../constants').GRAPHICS; var randomHueString = function() { var hue = Math.random(); @@ -568,7 +568,7 @@ var VisNode = VisBase.extend({ this.get('text').attr(attr.text); return; } - + var s = speed !== undefined ? speed : this.get('animationSpeed'); var e = easing || this.get('animationEasing'); diff --git a/src/visuals.js b/src/visuals/index.js similarity index 98% rename from src/visuals.js rename to src/visuals/index.js index 1b287092..d2efc1fc 100644 --- a/src/visuals.js +++ b/src/visuals/index.js @@ -1,12 +1,12 @@ -var Main = require('./app/main'); -var GRAPHICS = require('./constants').GRAPHICS; -var GLOBAL = require('./constants').GLOBAL; +var Main = require('../app/main'); +var GRAPHICS = require('../constants').GRAPHICS; +var GLOBAL = require('../constants').GLOBAL; -var Collections = require('./collections'); +var Collections = require('../collections'); var CommitCollection = Collections.CommitCollection; var BranchCollection = Collections.BranchCollection; -var Tree = require('./tree'); +var Tree = require('../tree'); var VisEdgeCollection = Tree.VisEdgeCollection; var VisBranchCollection = Tree.VisBranchCollection; var VisNode = Tree.VisNode; @@ -37,7 +37,7 @@ var Visualization = Backbone.View.extend({ paper: this.paper }); - var GitEngine = require('./git').GitEngine; + var GitEngine = require('../git').GitEngine; this.gitEngine = new GitEngine({ collection: this.commitCollection, branches: this.branchCollection,