diff --git a/IPython/frontend/html/notebook/static/css/notebook.css b/IPython/frontend/html/notebook/static/css/notebook.css
index a9f2862..dc2b0ac 100644
--- a/IPython/frontend/html/notebook/static/css/notebook.css
+++ b/IPython/frontend/html/notebook/static/css/notebook.css
@@ -104,7 +104,7 @@ div#notebook {
     width: 100%;
     /* This spaces the cell away from the edge of the notebook area */
     padding: 5px 5px 15px 5px;
-    margin: 0px
+    margin: 0px;
     background-color: white;
 }
 
@@ -137,6 +137,7 @@ div.cell {
 div.code_cell {
     background-color: white;
 }
+
 /* any special styling for code cells that are currently running goes here */
 div.code_cell.running {
 }
@@ -168,13 +169,50 @@ div.input_prompt {
     border-top: 1px solid transparent;
 }
 
-div.output {
+div.output_wrapper {
     /* This is a spacer between the input and output of each cell */
     margin-top: 5px;
+    margin-left: 5px;
+    /* FF needs explicit width to stretch */
+    width: 100%;
+    /* this position must be relative to enable descendents to be absolute within it */
+    position: relative;
+}
+
+/* class for the output area when it should be height-limited */
+div.output_scroll {
+  /* ideally, this would be max-height, but FF barfs all over that */
+  height: 24em;
+  /* FF needs this *and the wrapper* to specify full width, or it will shrinkwrap */
+  width: 100%;
+  
+  overflow: auto;
+  border-radius: 3px;
+  box-shadow: inset 0 2px 8px rgba(0, 0, 0, .8);
+}
+
+/* output div while it is collapsed */
+div.output_collapsed {
+  margin-right: 5px;
+}
+
+div.out_prompt_overlay {
+  height: 100%;
+  padding: 0px;
+  position: absolute;
+  border-radius: 3px;
+}
+
+div.out_prompt_overlay:hover {
+  /* use inner shadow to get border that is computed the same on WebKit/FF */
+  box-shadow: inset 0 0 1px #000;
+  background: rgba(240, 240, 240, 0.5);
 }
 
 div.output_prompt {
     color: darkred;
+    /* 5px right shift to account for margin in parent container */
+    margin: 0 5px 0 -5px;
 }
 
 /* This class is the outer container of all output sections. */
diff --git a/IPython/frontend/html/notebook/static/js/codecell.js b/IPython/frontend/html/notebook/static/js/codecell.js
index 5c5ead2..c4a1884 100644
--- a/IPython/frontend/html/notebook/static/js/codecell.js
+++ b/IPython/frontend/html/notebook/static/js/codecell.js
@@ -218,6 +218,11 @@ var IPython = (function (IPython) {
     };
 
 
+    CodeCell.prototype.toggle_output_scroll = function () {
+    this.output_area.toggle_scroll();
+    };
+
+
     CodeCell.prototype.set_input_prompt = function (number) {
         this.input_prompt_number = number;
         var ns = number || " ";
diff --git a/IPython/frontend/html/notebook/static/js/menubar.js b/IPython/frontend/html/notebook/static/js/menubar.js
index a809cfd..e2e6c61 100644
--- a/IPython/frontend/html/notebook/static/js/menubar.js
+++ b/IPython/frontend/html/notebook/static/js/menubar.js
@@ -158,6 +158,15 @@ var IPython = (function (IPython) {
         this.element.find('#toggle_output').click(function () {
             IPython.notebook.toggle_output();
         });
+        this.element.find('#collapse_all_output').click(function () {
+            IPython.notebook.collapse_all_output();
+        });
+        this.element.find('#scroll_all_output').click(function () {
+            IPython.notebook.scroll_all_output();
+        });
+        this.element.find('#expand_all_output').click(function () {
+            IPython.notebook.expand_all_output();
+        });
         this.element.find('#clear_all_output').click(function () {
             IPython.notebook.clear_all_output();
         });
diff --git a/IPython/frontend/html/notebook/static/js/notebook.js b/IPython/frontend/html/notebook/static/js/notebook.js
index 7c02bf7..2978047 100644
--- a/IPython/frontend/html/notebook/static/js/notebook.js
+++ b/IPython/frontend/html/notebook/static/js/notebook.js
@@ -194,7 +194,11 @@ var IPython = (function (IPython) {
                 return false;
             } else if (event.which === 79 && that.control_key_active) {
                 // Toggle output = o
-                that.toggle_output();
+                if (event.shiftKey){
+                    that.toggle_output_scroll();
+                } else {
+                    that.toggle_output();
+                }
                 that.control_key_active = false;
                 return false;
             } else if (event.which === 83 && that.control_key_active) {
@@ -883,6 +887,53 @@ var IPython = (function (IPython) {
     };
 
 
+    Notebook.prototype.toggle_output_scroll = function (index) {
+        var i = this.index_or_selected(index);
+        this.get_cell(i).toggle_output_scroll();
+    };
+
+
+    Notebook.prototype.collapse_all_output = function () {
+        var ncells = this.ncells();
+        var cells = this.get_cells();
+        for (var i=0; i<ncells; i++) {
+            if (cells[i] instanceof IPython.CodeCell) {
+                cells[i].output_area.collapse();
+            }
+        };
+        // this should not be set if the `collapse` key is removed from nbformat
+        this.dirty = true;
+    };
+
+
+    Notebook.prototype.scroll_all_output = function () {
+        var ncells = this.ncells();
+        var cells = this.get_cells();
+        for (var i=0; i<ncells; i++) {
+            if (cells[i] instanceof IPython.CodeCell) {
+                cells[i].output_area.expand();
+                cells[i].output_area.scroll_if_long(20);
+            }
+        };
+        // this should not be set if the `collapse` key is removed from nbformat
+        this.dirty = true;
+    };
+
+
+    Notebook.prototype.expand_all_output = function () {
+        var ncells = this.ncells();
+        var cells = this.get_cells();
+        for (var i=0; i<ncells; i++) {
+            if (cells[i] instanceof IPython.CodeCell) {
+                cells[i].output_area.expand();
+                cells[i].output_area.unscroll_area();
+            }
+        };
+        // this should not be set if the `collapse` key is removed from nbformat
+        this.dirty = true;
+    };
+
+
     Notebook.prototype.clear_all_output = function () {
         var ncells = this.ncells();
         var cells = this.get_cells();
diff --git a/IPython/frontend/html/notebook/static/js/outputarea.js b/IPython/frontend/html/notebook/static/js/outputarea.js
index 87af51c..d91e48b 100644
--- a/IPython/frontend/html/notebook/static/js/outputarea.js
+++ b/IPython/frontend/html/notebook/static/js/outputarea.js
@@ -16,28 +16,92 @@ var IPython = (function (IPython) {
 
     var OutputArea = function (selector, prompt_area) {
         this.selector = selector;
-        this.element = $(selector);
+        this.wrapper = $(selector);
         this.outputs = [];
         this.collapsed = false;
+        this.scrolled = false;
         this.clear_out_timeout = null;
         if (prompt_area === undefined) {
             this.prompt_area = true;
         } else {
             this.prompt_area = prompt_area;
         };
+        this.create_elements();
         this.style();
+        this.bind_events();
+    };
+    
+    OutputArea.prototype.create_elements = function () {
+        this.element = $("<div/>");
+        this.collapse_button = $("<div/>");
+        this.prompt_overlay = $("<div/>");
+        this.wrapper.append(this.prompt_overlay);
+        this.wrapper.append(this.element);
+        this.wrapper.append(this.collapse_button);
     };
 
 
     OutputArea.prototype.style = function () {
+        this.collapse_button.hide();
+        this.prompt_overlay.hide();
+        
+        this.wrapper.addClass('output_wrapper');
         this.element.addClass('output vbox');
+        
+        this.collapse_button.button();
+        this.collapse_button.addClass('output_collapsed vbox');
+        this.collapse_button.attr('title', 'click to expand outout');
+        this.collapse_button.html('. . .');
+        
+        this.prompt_overlay.addClass('out_prompt_overlay prompt');
+        this.prompt_overlay.attr('title', 'click to expand outout; double click to hide output');
+        
         this.collapse();
     };
 
 
+    OutputArea.prototype._should_scroll = function (lines) {
+        if (!lines) {
+            lines = 50;
+        }
+        // line-height from http://stackoverflow.com/questions/1185151
+        var fontSize = this.element.css('font-size');
+        var lineHeight = Math.floor(parseInt(fontSize.replace('px','')) * 1.5);
+        
+        return (this.element.height() > lines * lineHeight);
+    };
+
+
+    OutputArea.prototype.bind_events = function () {
+        var that = this;
+        this.prompt_overlay.dblclick(function () { that.toggle_output(); });
+        this.prompt_overlay.click(function () { that.toggle_scroll(); });
+
+        this.element.resize(function () {
+            // maybe scroll output,
+            // if it's grown large enough and hasn't already been scrolled.
+            if ( !that.scrolled && that._should_scroll()) {
+                that.scroll_area();
+            }
+        });
+        this.collapse_button.click(function () {
+            that.expand();
+        });
+        this.collapse_button.hover(function () {
+            $(this).addClass("ui-state-hover");
+        }, function () {
+            $(this).removeClass("ui-state-hover");
+        });
+    };
+
+
     OutputArea.prototype.collapse = function () {
         if (!this.collapsed) {
             this.element.hide();
+            this.prompt_overlay.hide();
+            if (this.element.html()){
+                this.collapse_button.show();
+            }
             this.collapsed = true;
         };
     };
@@ -45,7 +109,9 @@ var IPython = (function (IPython) {
 
     OutputArea.prototype.expand = function () {
         if (this.collapsed) {
+            this.collapse_button.hide();
             this.element.show();
+            this.prompt_overlay.show();
             this.collapsed = false;
         };
     };
@@ -60,6 +126,38 @@ var IPython = (function (IPython) {
     };
 
 
+    OutputArea.prototype.scroll_area = function () {
+        this.element.addClass('output_scroll');
+        this.prompt_overlay.attr('title', 'click to unscroll output; double click to hide');
+        this.scrolled = true;
+    };
+
+
+    OutputArea.prototype.unscroll_area = function () {
+        this.element.removeClass('output_scroll');
+        this.prompt_overlay.attr('title', 'click to scroll output; double click to hide');
+        this.scrolled = false;
+    };
+
+
+    OutputArea.prototype.scroll_if_long = function (lines) {
+        if (this._should_scroll(lines)) {
+            // only allow scrolling long-enough output
+            this.scroll_area();
+        };
+    };
+
+
+    OutputArea.prototype.toggle_scroll = function () {
+        if (this.scrolled) {
+            this.unscroll_area();
+        } else {
+            // only allow scrolling long-enough output
+            this.scroll_if_long(20);
+        };
+    };
+
+
     // typeset with MathJax if MathJax is available
     OutputArea.prototype.typeset = function () {
         if (window.MathJax){
@@ -132,6 +230,8 @@ var IPython = (function (IPython) {
             this.append_stream(json);
         };
         this.outputs.push(json);
+        var that = this;
+        setTimeout(function(){that.element.trigger('resize');}, 100);
     };
 
 
@@ -346,6 +446,7 @@ var IPython = (function (IPython) {
             // clear all, no need for logic
             output_div.html("");
             this.outputs = [];
+            this.unscroll_area();
             return;
         }
         // remove html output
@@ -360,7 +461,8 @@ var IPython = (function (IPython) {
         if (other) {
             output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
         }
-
+        this.unscroll_area();
+        
         // remove cleared outputs from JSON list:
         for (var i = this.outputs.length - 1; i >= 0; i--) {
             var out = this.outputs[i];
diff --git a/IPython/frontend/html/notebook/static/js/quickhelp.js b/IPython/frontend/html/notebook/static/js/quickhelp.js
index 3376883..d609e70 100644
--- a/IPython/frontend/html/notebook/static/js/quickhelp.js
+++ b/IPython/frontend/html/notebook/static/js/quickhelp.js
@@ -35,6 +35,7 @@ var IPython = (function (IPython) {
             {key: 'Ctrl-m a', help: 'insert cell above'},
             {key: 'Ctrl-m b', help: 'insert cell below'},
             {key: 'Ctrl-m o', help: 'toggle output'},
+            {key: 'Ctrl-m O', help: 'toggle output scroll'},
             {key: 'Ctrl-m l', help: 'toggle line numbers'},
             {key: 'Ctrl-m s', help: 'save notebook'},
             {key: 'Ctrl-m j', help: 'move cell down'},
diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html
index 64da27c..26eb12f 100644
--- a/IPython/frontend/html/notebook/templates/notebook.html
+++ b/IPython/frontend/html/notebook/templates/notebook.html
@@ -117,8 +117,15 @@ data-notebook-id={{notebook_id}}
                 <li id="to_heading5"><a href="#">Heading 5</a></li>
                 <li id="to_heading6"><a href="#">Heading 6</a></li>
                 <hr/>
-                <li id="toggle_output"><a href="#">Toggle Output</a></li>
-                <li id="clear_all_output"><a href="#">Clear All Output</a></li>
+                <li id="toggle_output"><a href="#">Toggle Current Output</a></li>
+                <li id="all_outputs"><a href="#">All Output</a>
+                    <ul>
+                        <li id="expand_all_output"><a href="#">Expand</a></li>
+                        <li id="scroll_all_output"><a href="#">Scroll Long</a></li>
+                        <li id="collapse_all_output"><a href="#">Collapse</a></li>
+                        <li id="clear_all_output"><a href="#">Clear</a></li>
+                    </ul>
+                </li>
             </ul>
         </li>
         <li><a href="#">Kernel</a>