diff --git a/src/js/level/index.js b/src/js/level/index.js index 200c3b2f..96ba35ee 100644 --- a/src/js/level/index.js +++ b/src/js/level/index.js @@ -50,6 +50,9 @@ var Level = Sandbox.extend({ this.initGoalData(options); this.initName(options); + this.on('toggleGoal', this.toggleGoal); + this.on('minimizeCanvas', this.minimizeGoal); + this.on('resizeCanvas', this.resizeGoal); Level.__super__.initialize.apply(this, [options]); this.startOffCommand(); @@ -128,7 +131,8 @@ var Level = Sandbox.extend({ var name = intl.getName(this.level); this.levelToolbar = new LevelToolbar({ - name: name + name: name, + parent: this }); }, @@ -170,7 +174,8 @@ var Level = Sandbox.extend({ var onlyMaster = TreeCompare.onlyMasterCompared(this.level); // first we make the goal visualization holder this.goalCanvasHolder = new CanvasTerminalHolder({ - text: (onlyMaster) ? intl.str('goal-only-master') : undefined + text: (onlyMaster) ? intl.str('goal-only-master') : undefined, + parent: this }); // then we make a visualization. the "el" here is the element to @@ -185,9 +190,38 @@ var Level = Sandbox.extend({ levelBlob: this.level, noClick: true }); + + // If the goal visualization gets dragged to the right side of the screen, then squeeze the main + // repo visualization a bit to make room. This way, you could have the goal window hang out on + // the right side of the screen and still see the repo visualization. + this.goalVis.customEvents.on('drag', _.bind(function(event, ui) { + if (ui.position.left > 0.5*$(window).width()) { + if (!$('#goalPlaceholder').is(':visible')) { + $('#goalPlaceholder').show(); + this.mainVis.myResize(); + } + } else { + if ($('#goalPlaceholder').is(':visible')) { + $('#goalPlaceholder').hide(); + this.mainVis.myResize(); + } + } + }, this)); + return this.goalCanvasHolder; }, + minimizeGoal: function (position, size) { + this.goalVis.hide(); + this.goalWindowPos = position; + this.goalWindowSize = size; + this.levelToolbar.$goalButton.text('Show Goal'); + }, + + resizeGoal: function () { + this.goalVis.myResize(); + }, + showSolution: function(command, deferred) { var toIssue = this.level.solutionCommand; var issueFunc = _.bind(function() { @@ -230,8 +264,17 @@ var Level = Sandbox.extend({ }); }, + toggleGoal: function () { + if (this.goalCanvasHolder && this.goalCanvasHolder.inDom) { + this.hideGoal(); + } else { + this.showGoal(); + } + }, + showGoal: function(command, defer) { this.showSideVis(command, defer, this.goalCanvasHolder, this.initGoalVisualization); + this.levelToolbar.$goalButton.text('Hide Goal'); }, showSideVis: function(command, defer, canvasHolder, initMethod) { @@ -242,12 +285,13 @@ var Level = Sandbox.extend({ canvasHolder = initMethod.apply(this); } - canvasHolder.slideIn(); + canvasHolder.restore(this.goalWindowPos, this.goalWindowSize); setTimeout(safeFinish, canvasHolder.getAnimationTime()); }, hideGoal: function(command, defer) { this.hideSideVis(command, defer, this.goalCanvasHolder); + this.levelToolbar.$goalButton.text('Show Goal'); }, hideSideVis: function(command, defer, canvasHolder, vis) { diff --git a/src/js/views/index.js b/src/js/views/index.js index 790089ca..cba5e06e 100644 --- a/src/js/views/index.js +++ b/src/js/views/index.js @@ -558,6 +558,7 @@ var LevelToolbar = BaseView.extend({ initialize: function(options) { options = options || {}; + this.parent = options.parent; this.JSON = { name: options.name || 'Some level! (unknown name)' }; @@ -565,6 +566,13 @@ var LevelToolbar = BaseView.extend({ this.beforeDestination = $($('#commandLineHistory div.toolbar')[0]); this.render(); + this.$goalButton = this.$el.find('#show-goal'); + + var parent = this.parent; + this.$goalButton.on('click', function () { + parent.trigger('toggleGoal'); + }); + if (!options.wait) { process.nextTick(_.bind(this.show, this)); } @@ -840,6 +848,8 @@ var CanvasTerminalHolder = BaseView.extend({ initialize: function(options) { options = options || {}; + this.parent = options.parent; + this.minHeight = options.minHeight || 200; this.destination = $('body'); this.JSON = { title: options.title || intl.str('goal-to-reach'), @@ -849,6 +859,19 @@ var CanvasTerminalHolder = BaseView.extend({ this.render(); this.inDom = true; + this.$terminal = this.$el.find('.terminal-window-holder').first(); + this.$terminal.height(0.8 * $(window).height()); + this.$terminal.draggable({ + cursor: 'move', + handle: '.toolbar', + containment: '#interfaceWrapper', + scroll: false + }); + + // If the entire window gets resized such that the terminal is outside the view, then + // move it back into the view, and expand/shrink it vertically as necessary. + $(window).on('resize', _.debounce(_.bind(this.recalcLayout, this), 300)); + if (options.additionalClass) { this.$el.addClass(options.additionalClass); } @@ -861,7 +884,7 @@ var CanvasTerminalHolder = BaseView.extend({ }, die: function() { - this.slideOut(); + this.minimize(); this.inDom = false; setTimeout(_.bind(function() { @@ -869,16 +892,79 @@ var CanvasTerminalHolder = BaseView.extend({ }, this), this.getAnimationTime()); }, - slideOut: function() { - this.slideToggle(true); + minimize: function() { + this.parent.trigger('minimizeCanvas', { + left: this.$terminal.css('left'), + top: this.$terminal.css('top') + }, { + width: this.$terminal.css('width'), + height: this.$terminal.css('height') + }); + + this.$terminal.animate({ + height: '0px', + opacity: 0 + }, this.getAnimationTime()); }, - slideIn: function() { - this.slideToggle(false); + restore: function (pos, size) { + var self = this; + pos = pos || { top: this.$terminal.css('top'), left: this.$terminal.css('left') }; + size = size || { width: this.$terminal.css('width'), height: this.$terminal.css('height') }; + + this.$terminal.css({ + top: pos.top, + left: pos.left, + width: size.width, + height: '0px', + opacity: '0' + }); + + this.$terminal.animate({ + height: size.height, + opacity: 1 + }, this.getAnimationTime(), function () { + self.recalcLayout(); + }); }, - slideToggle: function(value) { - this.$('div.terminal-window-holder').toggleClass('slideOut', value); + recalcLayout: function () { + // Resize/reposition self based on the size of the browser window. + + var parent = this.parent, + leftOffset = 0, + topOffset = 0, + heightOffset = 0, + width = this.$terminal.outerWidth(), + height = this.$terminal.outerHeight(), + left = this.$terminal.offset().left, + top = this.$terminal.offset().top, + right = ($(window).width() - (left + width)), + bottom = ($(window).height() - (top + height)), + minHeight = 0.75 * $(window).height(), + maxHeight = 0.95 * $(window).height(); + + // Calculate offsets + if (top < 0) { topOffset = -top; } + if (left < 0) { leftOffset = -left; } + if (right < 0) { leftOffset = right; } + if (bottom < 0) { topOffset = bottom; } + if (height < minHeight) { heightOffset = minHeight - height; } + if (height > maxHeight) { heightOffset = maxHeight - height; } + + // Establish limits + left = Math.max(left + leftOffset, 0); + top = Math.max(top + topOffset, 0); + height = Math.max(height + heightOffset, minHeight); + + // Set the new position/size + this.$terminal.animate({ + left: left + 'px', + top: top + 'px', + height: height + 'px' + }, this.getAnimationTime(), function () { + parent.trigger('resizeCanvas'); + }); }, getCanvasLocation: function() { diff --git a/src/js/visuals/visualization.js b/src/js/visuals/visualization.js index bb0c4ff5..b385c992 100644 --- a/src/js/visuals/visualization.js +++ b/src/js/visuals/visualization.js @@ -74,6 +74,13 @@ var Visualization = Backbone.View.extend({ this.myResize(); }, this)); + // If the visualization is within a draggable container, we need to update the + // position whenever the container is moved. + this.$el.parents('.ui-draggable').on('drag', _.bind(function(event, ui) { + this.customEvents.trigger('drag', event, ui); + this.myResize(); + }, this)); + this.gitVisuals.drawTreeFirstTime(); if (this.treeString) { this.gitEngine.loadTreeFromString(this.treeString); @@ -182,12 +189,14 @@ var Visualization = Backbone.View.extend({ $(this.paper.canvas).css('visibility', 'visible'); setTimeout(_.bind(this.fadeTreeIn, this), 10); this.originToo('show', arguments); + this.myResize(); }, showHarsh: function() { $(this.paper.canvas).css('visibility', 'visible'); this.setTreeOpacity(1); this.originToo('showHarsh', arguments); + this.myResize(); }, resetFromThisTreeNow: function(treeString) { @@ -257,8 +266,8 @@ var Visualization = Backbone.View.extend({ // if we don't have a container, we need to set our // position absolutely to whatever we are tracking if (!this.containerElement) { - var left = el.offsetLeft; - var top = el.offsetTop; + var left = this.$el.offset().left; + var top = this.$el.offset().top; $(this.paper.canvas).css({ position: 'absolute', diff --git a/src/style/main.css b/src/style/main.css index 80a4d6bc..d64fb5e5 100644 --- a/src/style/main.css +++ b/src/style/main.css @@ -34,7 +34,7 @@ p { } .githubLink { - z-index: 99; + z-index: 2; position: fixed; top: 0; right: 0; @@ -224,10 +224,11 @@ body.hgMode #mainVisSpace .modeText.hgMode { /* Some interface things */ div.canvasTerminalHolder { - height: 100%; + height: 0; position: fixed; top: 0; left: 0; + z-index: 3; } #canvasHolder { @@ -254,28 +255,8 @@ body.hgMode .visBackgroundColor { min-height: 600px; } -div.canvasTerminalHolder > div.terminal-window-holder { - margin: 100px 0; - height: 100%; - -webkit-transform: translate3d(0,0,0); - -moz-transform: translate3d(0,0,0); - -o-transform: translate3d(0,0,0); - -ms-transform: translate3d(0,0,0); - transform: translate3d(0,0,0); -} - -div.canvasTerminalHolder > div.terminal-window-holder.slideOut { - -webkit-transform: translate3d(-150%,0,0); - -moz-transform: translate3d(-150%,0,0); - -o-transform: translate3d(-150%,0,0); - -ms-transform: translate3d(-150%,0,0); - transform: translate3d(-150%,0,0); -} - - div.canvasTerminalHolder > div.terminal-window-holder > div.wrapper { - margin: 0 20px 0px 20px; - height: 80%; + height: 100%; box-shadow: 0 0 30px rgb(0,0,0); cursor: pointer; border-radius: 0 0 5px 5px; @@ -346,6 +327,21 @@ div.toolbar.level-toolbar { background-image: linear-gradient(top, #B2FF2E, #8AD247); border-radius: 0; height: 50px; + font-size: 12px; +} + +div.toolbar.level-toolbar button { + font-size: 12px; + border: 1px solid #888; + border-radius: 4px; + padding: 4px 8px; + background-image: -webkit-linear-gradient(top, #EFEDEE, #C1C1C1); +} + +#show-goal { + position: absolute; + top: 10px; + left: 4px; } span.levelToolbarSpan { @@ -739,6 +735,10 @@ div.modalView.box.inFront.show { z-index: 100; } +.terminal-window-holder { + z-index: 3; +} + .terminal-window .inside { padding: 10px; box-shadow: 0 0 10px rgba(0, 0, 0, 0.7); diff --git a/src/template.index.html b/src/template.index.html index 2f1ee1ca..74cd966e 100644 --- a/src/template.index.html +++ b/src/template.index.html @@ -67,7 +67,7 @@ -
+
Git @@ -75,6 +75,12 @@ Hg
+ + + +
@@ -138,6 +144,7 @@