diff --git a/src/commandline.js b/src/commandline.js deleted file mode 100644 index 2ddf7370..00000000 --- a/src/commandline.js +++ /dev/null @@ -1,255 +0,0 @@ -var Command = Backbone.Model.extend({ - defaults: { - status: 'inqueue', - result: '', - error: null, - generalArgs: [], - supportedMap: {}, - options: null, - method: null, - createTime: null, - rawStr: null - }, - - validateAtInit: function() { - if (!this.get('rawStr')) { - throw new Error('Give me a string!'); - } - if (!this.get('createTime')) { - this.set('createTime', new Date().toString()); - } - this.on('change:error', this.errorChanged, this); - }, - - initialize: function() { - this.validateAtInit(); - this.parseOrCatch(); - }, - - parseOrCatch: function() { - try { - this.parse(); - } catch (err) { - if (err instanceof CommandProcessError || - err instanceof GitError) { - this.set('status', 'error'); - this.set('error', err); - } else if (err instanceof CommandResult) { - this.set('status', 'finished'); - this.set('error', err); - } else { - throw err; - } - } - }, - - errorChanged: function(model, err) { - this.set('err', err); - this.set('status', 'error'); - this.formatError(); - }, - - formatError: function() { - this.set('result', this.get('err').toResult()); - }, - - getShortcutMap: function() { - return { - 'git commit': /^gc($|\s)/, - 'git add': /^ga($|\s)/, - 'git checkout': /^gchk($|\s)/, - 'git rebase': /^gr($|\s)/, - 'git branch': /^gb($|\s)/ - }; - }, - - getRegexMap: function() { - return { - // ($|\s) means that we either have to end the string - // after the command or there needs to be a space for options - commit: /^commit($|\s)/, - add: /^add($|\s)/, - checkout: /^checkout($|\s)/, - rebase: /^rebase($|\s)/, - reset: /^reset($|\s)/, - branch: /^branch($|\s)/, - revert: /^revert($|\s)/, - merge: /^merge($|\s)/ - }; - }, - - getSandboxCommands: function() { - return [ - [/^ls/, function() { - throw new CommandResult({ - msg: "DontWorryAboutFilesInThisDemo.txt" - }); - }], - [/^cd/, function() { - throw new CommandResult({ - msg: "Directory Changed to '/directories/dont/matter/in/this/demo'" - }); - }], - [/^git$/, function() { - // TODO better git description. also help, hint, etc - throw new CommandResult({ - msg: _.escape("\ - Git Version \n \ - PCOTTLE.1.0 \ - Usage: \n \ - git [] \ - ") - }); - }] - ]; - }, - - parse: function() { - var str = this.get('rawStr'); - // first if the string is empty, they just want a blank line - if (!str.length) { - throw new CommandResult({msg: ""}); - } - - // then check if it's one of our sandbox commands - _.each(this.getSandboxCommands(), function(tuple) { - var regex = tuple[0]; - if (regex.exec(str)) { - tuple[1](); - } - }); - - // then check if shortcut exists, and replace, but - // preserve options if so - _.each(this.getShortcutMap(), function(regex, method) { - var results = regex.exec(str); - if (results) { - str = method + ' ' + str.slice(results[0].length); - } - }); - - // see if begins with git - if (str.slice(0,3) !== 'git') { - throw new CommandProcessError({ - msg: 'Git commands only, sorry!' - }); - } - - // ok, we have a (probably) valid command. actually parse it - this.gitParse(str); - }, - - gitParse: function(str) { - // now slice off command part - var fullCommand = str.slice('git '.length); - - // see if we support this particular command - _.each(this.getRegexMap(), function(regex, method) { - if (regex.exec(fullCommand)) { - this.set('options', fullCommand.slice(method.length + 1)); - this.set('method', method); - // we should stop iterating, but the regex will only match - // one command in practice. we could stop iterating if we used - // jqeurys for each but im using underscore (for no real reason other - // than style) - } - }, this); - - if (!this.get('method')) { - throw new CommandProcessError({ - msg: "Sorry, this demo does not support that git command: " + fullCommand - }); - } - - // parse off the options and assemble the map / general args - var optionParser = new OptionParser(this.get('method'), this.get('options')); - - // steal these away so we can be completely JSON - this.set('generalArgs', optionParser.generalArgs); - this.set('supportedMap', optionParser.supportedMap); - }, -}); - -/** - * OptionParser - */ -function OptionParser(method, options) { - this.method = method; - this.rawOptions = options; - - this.supportedMap = this.getMasterOptionMap()[method]; - if (this.supportedMap === undefined) { - throw new Error('No option map for ' + method); - } - - this.generalArgs = []; - this.explodeAndSet(); -} - -OptionParser.prototype.getMasterOptionMap = function() { - // here a value of false means that we support it, even if its just a - // pass-through option. If the value is not here (aka will be undefined - // when accessed), we do not support it. - return { - commit: { - '--amend': false, - '-a': false, // warning - '-am': false - }, - add: {}, - branch: { - '-d': false, - '-D': false - }, - checkout: { - '-b': false - }, - reset: { - '--hard': false, - '--soft': false, // this will raise an error but we catch it in gitEngine - }, - merge: {}, - rebase: {}, - revert: {} - }; -}; - -OptionParser.prototype.explodeAndSet = function() { - // split on spaces, except when inside quotes, and strip quotes after. - // for some reason the regex includes the quotes even if i move the parantheses - // inside - var exploded = this.rawOptions.match(/('.*?'|".*?"|\S+)/g) || []; - _.each(exploded, function(part, i) { - exploded[i] = part.replace(/['"]/g, ''); - }); - - for (var i = 0; i < exploded.length; i++) { - var part = exploded[i]; - if (part.slice(0,1) == '-') { - // it's an option, check supportedMap - if (this.supportedMap[part] === undefined) { - throw new CommandProcessError({ - msg: 'The option "' + part + '" is not supported' - }); - } - - // go through and include all the next args until we hit another option or the end - var optionArgs = []; - var next = i + 1; - while (next < exploded.length && exploded[next].slice(0,1) != '-') { - optionArgs.push(exploded[next]); - next += 1; - } - i = next - 1; - - // **phew** we are done grabbing those. theseArgs is truthy even with an empty array - this.supportedMap[part] = optionArgs; - } else { - // must be a general arg - this.generalArgs.push(part); - } - } - - // done! -}; - diff --git a/src/git.js b/src/git.js index 01b2edfc..0e44dfcc 100644 --- a/src/git.js +++ b/src/git.js @@ -1,4 +1,3 @@ - // backbone or something uses _.uniqueId, so we make our own here var uniqueId = (function() { var n = 0; @@ -799,7 +798,8 @@ var Branch = Ref.extend({ var Commit = Backbone.Model.extend({ defaults: { type: 'commit', - children: [] + children: [], + parents: [] }, validateAtInit: function() { @@ -808,9 +808,7 @@ var Commit = Backbone.Model.extend({ } // root commits have no parents - if (this.get('rootCommit')) { - this.set('parents', []); - } else { + if (!this.get('rootCommit')) { if (!this.get('parents') || !this.get('parents').length) { throw new Error('needs parents'); } diff --git a/src/index.html b/src/index.html index 7d84e5d6..ff5a8e07 100644 --- a/src/index.html +++ b/src/index.html @@ -10,23 +10,34 @@ - + +
+
+
+
-
-
-
+
+
+
+
+ +
+
-
- + +
+
+ - - + - + + - - - + + + + + + + + + + + diff --git a/src/style/main.css b/src/style/main.css index b8ab7d82..697ff571 100644 --- a/src/style/main.css +++ b/src/style/main.css @@ -1,18 +1,62 @@ -html, body { - overflow:hidden; +html { + height: 100%; +} +body { + margin: 0px; + border: 0px; + padding: 0px; +} + +html, body { + /* background: -moz-radial-gradient(center, ellipse cover, #0066cc 0%, #000000 90%); background: -webkit-gradient(radial, center center, 0px, center center, 100%, color-stop(0%,#0066cc), color-stop(90%,#000000)); background: -webkit-radial-gradient(center, ellipse cover, #0066cc 0%,#000 90%); background: -o-radial-gradient(center, ellipse cover, #0066cc 0%,#000000 90%); background: -ms-radial-gradient(center, ellipse cover, #0066cc 0%,#000000 90%); - background: radial-gradient(center, ellipse cover, #0066cc 0%,#000000 90%); - -webkit-perspective:600px; + background: radial-gradient(center, ellipse cover, #0066cc 0%,#000000 90%); */ + + -webkit-perspective: 600px; font-family: Monaco, Courier, font-monospace; color: #eee; } +/* Box Model */ +html, +body, +div.box { + display: -webkit-box; +} + +div.flex0 { + -webkit-box-flex: 0; +} + +body, +div.flex1 { + -webkit-box-flex: 1; +} + +div.flex2 { + -webkit-box-flex: 2; +} + +div.flex3 { + -webkit-box-flex: 3; +} + +html, +body, +div.vertical { + -webkit-box-orient: vertical; +} + +div.horizontal { + -webkit-box-orient: horizontal; +} + /* Transition */ .transitionBackground { -webkit-transition: background 700ms cubic-bezier(0.260, 0.860, 0.440, 0.985); @@ -30,17 +74,21 @@ html, body { transition: opacity 1700ms cubic-bezier(0.260, 0.860, 0.440, 0.985); } +#treeCanvas { + border: 3px solid black; +} + +#interfaceWrapper { + min-width: 600px; +} + #controls { - position: absolute; - top: 0px; - left: 0px; - width: 50%; - height: 100%; - -webkit-box-orientation: vertical; +} + +#canvasWrapper { } #commandTextField { - width: 90%; } p.commandLine, p.commandLineResult { @@ -103,8 +151,6 @@ p.commandLine span.arrows { } #commandLineHistory { - width: 100%; - height: 200px; overflow-y: scroll; background: #000; opacity: 0.85; diff --git a/src/tree.js b/src/tree.js index e69de29b..fed1ec59 100644 --- a/src/tree.js +++ b/src/tree.js @@ -0,0 +1,5 @@ +var VisNode = Backbone.Model.extend({ +}); + +var VisEdge = Backbone.Model.extend({ +}); diff --git a/src/views.js b/src/views.js deleted file mode 100644 index 411dd25d..00000000 --- a/src/views.js +++ /dev/null @@ -1,187 +0,0 @@ -var CommandPromptView = Backbone.View.extend({ - initialize: function(options) { - this.collection = options.collection; - this.commands = []; - this.index = -1; - }, - - events: { - 'keyup #commandTextField': 'keyUp' - }, - - keyUp: function(e) { - // we need to capture some of these events. - // WARNING: this key map is not internationalized :( - var keyMap = { - // enter - 13: _.bind(function() { - this.submit(); - }, this), - // up - 38: _.bind(function() { - this.commandSelectChange(1); - }, this), - // down - 40: _.bind(function() { - this.commandSelectChange(-1); - }, this) - }; - - if (keyMap[e.which] !== undefined) { - e.preventDefault(); - keyMap[e.which](); - } - }, - - commandSelectChange: function(delta) { - this.index += delta; - - // if we are over / under, display blank line. yes this eliminates your - // partially written command, but i doubt that is much in this demo - if (this.index >= this.commands.length || this.index < 0) { - this.clear(); - this.index = -1; - return; - } - - // yay! we actually can display something - this.setTextField(this.commands[this.index]); - }, - - setTextField: function(value) { - this.$('#commandTextField').val(value); - }, - - clear: function() { - this.setTextField(''); - }, - - submit: function() { - var value = this.$('#commandTextField').val().replace('\n', ''); - this.clear(); - - // if we are entering a real command, add it to our history - if (value.length) { - this.commands.unshift(value); - } - this.index = -1; - - // split commands on semicolon - _.each(value.split(';'), _.bind(function(command) { - command = command.replace(/^(\s+)/, ''); - command = command.replace(/(\s+)$/, ''); - if (command.length) { - this.addToCollection(command); - } - }, this)); - }, - - addToCollection: function(value) { - var command = new Command({ - rawStr: value - }); - this.collection.add(command); - } -}); - - -// This is the view for all commands -- it will represent -// their status (inqueue, processing, finished, error), -// their value ("git commit --amend"), -// and the result (either errors or warnings or whatever) -var CommandView = Backbone.View.extend({ - tagName: 'div', - model: Command, - template: _.template($('#command-template').html()), - - events: { - 'click': 'clicked' - }, - - clicked: function(e) { - console.log('was clicked'); - }, - - initialize: function() { - this.model.bind('change', this.wasChanged, this); - this.model.bind('destroy', this.remove, this); - }, - - wasChanged: function(model, changeEvent) { - console.log('command changed', model, changeEvent); - // for changes that are just comestic, we actually only want to toggle classes - // with jquery rather than brutally delete a html of HTML - var changes = changeEvent.changes; - var changeKeys = _.keys(changes); - if (_.difference(changeKeys, ['status']) == 0) { - this.updateStatus(); - } else if (_.difference(changeKeys, ['error']) == 0) { - // the above will - this.render(); - } else { - this.render(); - } - }, - - updateStatus: function() { - var statuses = ['inqueue', 'processing', 'finished']; - var toggleMap = {}; - _.each(statuses, function(status) { - toggleMap[status] = false; - }); - toggleMap[this.model.get('status')] = true; - - var query = this.$('p.commandLine'); - - _.each(toggleMap, function(value, key) { - query.toggleClass(key, value); - }); - }, - - render: function() { - var json = _.extend( - { - resultType: '', - result: '' - }, - this.model.toJSON() - ); - - this.$el.html(this.template(json)); - return this; - }, - - remove: function() { - $(this.el).hide(); - } -}); - - -var CommandLineHistoryView = Backbone.View.extend({ - initialize: function(options) { - this.collection = options.collection; - - this.collection.on('add', this.addOne, this); - this.collection.on('reset', this.addAll, this); - this.collection.on('all', this.render, this); - - this.collection.on('change', this.scrollDown, this); - }, - - scrollDown: function() { - var el = $('#commandLineHistory')[0]; - el.scrollTop = el.scrollHeight; - }, - - addOne: function(command) { - var view = new CommandView({ - model: command - }); - this.$('#commandDisplay').append(view.render().el); - this.scrollDown(); - }, - - addAll: function() { - this.collection.each(this.addOne); - } -}); diff --git a/src/visuals.js b/src/visuals.js index 6a70ac2a..63c8c373 100644 --- a/src/visuals.js +++ b/src/visuals.js @@ -3,13 +3,14 @@ function GitVisuals(options) { this.nodeMap = {}; this.collection.on('change', _.bind(this.collectionChanged, this)); - events.on('drawGitVisuals', _.bind(this.drawVisuals, this)); - events.on('fixNodePositions', _.bind(this.fixNodes, this)); } GitVisuals.prototype.addNode = function(id) { - var visNode = sys.addNode(id); + var visNode = new VisNode({ + id: id + }); this.nodeMap[id] = visNode; + return visNode; }; @@ -22,88 +23,13 @@ GitVisuals.prototype.addEdge = function(idTail, idHead) { ', ' + idHead + ') does not exist'); } - sys.addEdge(visNodeTail, visNodeHead); -}; - -GitVisuals.prototype.drawVisuals = function(sys, ctx, canvas) { - this.drawRefs(sys, ctx, canvas); -}; - -GitVisuals.prototype.fixNodes = function(sys) { - this.fixRootCommit(sys); -}; - -GitVisuals.prototype.drawRefs = function(sys, ctx, canvas) { - var sFill = graphics.refSelectedFontFill; - // we need to draw refs here - var branches = gitEngine.getBranches(); - var detachedHead = gitEngine.getDetachedHead(); - var HEAD = gitEngine.getHead(); - - _.forEach(branches, _.bind(function(branch) { - // get the location of the arbor node and then somehow draw the ref to the side? - var node = branch.target.get('visNode'); - var fillStyle = branch.selected ? sFill : undefined; - this.drawLabel(ctx, sys, node, branch.id, fillStyle); - }, this)); - - if (detachedHead) { - var node = HEAD.get('target').get('visNode'); - this.drawLabel(ctx, sys, node, 'HEAD', sFill); - } -}; - -GitVisuals.prototype.drawLabel = function(ctx, sys, node, name, fillStyle) { - fillStyle = fillStyle || graphics.refFontFill; - - var nodePoint = sys.toScreen(node._p); - // text position - // TODO: better positioning of text here - var screenPoint = _.clone(nodePoint); - screenPoint.x += 100; - - ctx.font = graphics.refFont; - ctx.fillStyle = fillStyle; - ctx.fillText(name, screenPoint.x, screenPoint.y); - - // also draw an arrow - var offset = Math.round(graphics.nodeRadius * 2.5); - this.drawArrow(ctx, screenPoint, nodePoint, graphics.arrowHeadWidth, offset); -}; - -GitVisuals.prototype.drawArrow = function(ctx, start, end, headWidth, offset) { - // TODO only horizontal arrows for now, fix this later - var end = _.clone(end); - end.x += offset; - - ctx.lineWidth = graphics.arrowWidth; - ctx.fillStyle = graphics.arrowFill; - ctx.strokeStyle = graphics.arrowStroke; - - ctx.beginPath(); - ctx.moveTo(start.x, start.y); - ctx.lineTo(end.x, end.y); - - // now do the little arrow head - ctx.lineTo(end.x + headWidth, end.y + headWidth); - ctx.lineTo(end.x + headWidth, end.y - headWidth); - ctx.lineTo(end.x, end.y); - - ctx.stroke(); + var edge = new VisEdge({ + tail: visNodeTail, + head: visNodeHead + }); }; GitVisuals.prototype.collectionChanged = function() { - // redo the algorithms + // redo stuff }; -GitVisuals.prototype.fixRootCommit = function(sys) { - // get the viewports bottom center - var bottomPosScreen = { - x: Math.round($('#viewport').width() * 0.5), - y: $('#viewport').height() - graphics.nodeRadius * 2.5 - }; - - var bottomPos = sys.fromScreen(bottomPosScreen); - // fix the root commit to the bottom - gitEngine.rootCommit.get('visNode').p = bottomPos; -};