diff --git a/IPython/frontend/html/notebook/static/css/notebook.css b/IPython/frontend/html/notebook/static/css/notebook.css index 88f9259d4..19984b0b0 100644 --- a/IPython/frontend/html/notebook/static/css/notebook.css +++ b/IPython/frontend/html/notebook/static/css/notebook.css @@ -172,7 +172,7 @@ div.output_area { /* This class is for the output subarea inside the output_area and after the prompt div. */ div.output_subarea { - padding: 0.4em 6.1em 0.4em 0.4em; + padding: 0.4em 0.4em 0.4em 0.4em; } /* The rest of the output_* classes are for special styling of the different @@ -257,11 +257,11 @@ div.text_cell_render { .ansigrey {color: grey;} .ansibold {font-weight: bold;} -.completions , .tooltip { +.completions { position: absolute; z-index: 10; overflow: auto; - border: 1px solid black; + border: 1px solid grey; } .completions select { @@ -273,61 +273,11 @@ div.text_cell_render { font-family: monospace; } -@-moz-keyframes fadeIn { - from {opacity:0;} - to {opacity:1;} +option.context { + background-color: #DEF7FF; } - -@-webkit-keyframes fadeIn { - from {opacity:0;} - to {opacity:1;} -} - -@keyframes fadeIn { - from {opacity:0;} - to {opacity:1;} -} - -/*"close" "expand" and "Open in pager button" of -/* the tooltip*/ -.tooltip a { - float:right; -} - -/*properties of tooltip after "expand"*/ -.bigtooltip { - height:30%; -} - -/*properties of tooltip before "expand"*/ -.smalltooltip { - text-overflow: ellipsis; - overflow: hidden; - height:15%; -} - -.tooltip { - /*transition when "expand"ing tooltip */ - -webkit-transition-property: height; - -webkit-transition-duration: 1s; - -moz-transition-property: height; - -moz-transition-duration: 1s; - transition-property: height; - transition-duration: 1s; - max-width:700px; - border-radius: 0px 10px 10px 10px; - box-shadow: 3px 3px 5px #999; - /*fade-in animation when inserted*/ - -webkit-animation: fadeIn 200ms; - -moz-animation: fadeIn 200ms; - animation: fadeIn 200ms; - vertical-align: middle; - background: #FDFDD8; - outline: none; - padding: 3px; - margin: 0px; - font-family: monospace; - min-height:50px; +option.introspection { + background-color: #EBF4EB; } /*fixed part of the completion*/ diff --git a/IPython/frontend/html/notebook/static/css/tooltip.css b/IPython/frontend/html/notebook/static/css/tooltip.css new file mode 100644 index 000000000..3c65498d3 --- /dev/null +++ b/IPython/frontend/html/notebook/static/css/tooltip.css @@ -0,0 +1,133 @@ +/** + * Primary styles + * + * Author: IPython Development Team + */ +/** WARNING IF YOU ARE EDITTING THIS FILE, if this is a .css file, It has a lot + * of chance of beeing generated from the ../less/[samename].less file, you can + * try to get back the less file by reverting somme commit in history + **/ +/* + * We'll try to get something pretty, so we + * have some strange css to have the scroll bar on + * the left with fix button on the top right of the tooltip + */ +@-moz-keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +@-webkit-keyframes fadeOut { + from { + opacity: 1; + } + to { + opacity: 0; + } +} +@-moz-keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +@-webkit-keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} +.tooltip a { + float: right; +} +/*properties of tooltip after "expand"*/ +.bigtooltip { + overflow: auto; + height: 200px; + -webkit-transition-property: height; + -webkit-transition-duration: 1s; + -moz-transition-property: height; + -moz-transition-duration: 1s; + transition-property: height; + transition-duration: 1s; +} +/*properties of tooltip before "expand"*/ +.smalltooltip { + -webkit-transition-property: height; + -webkit-transition-duration: 1s; + -moz-transition-property: height; + -moz-transition-duration: 1s; + transition-property: height; + transition-duration: 1s; + text-overflow: ellipsis; + overflow: hidden; + height: 80px; +} +.tooltipbuttons { + position: absolute; + padding-right: 15px; + top: 0px; + right: 0px; +} +.tooltiptext { + /*avoid the button to overlap on some docstring*/ + + padding-right: 30px; +} +.tooltip { + max-width: 700px; + border-radius: 4px; + -moz-box-shadow: 0px 6px 10px -1px #adadad; + -webkit-box-shadow: 0px 6px 10px -1px #adadad; + box-shadow: 0px 6px 10px -1px #adadad; + /*fade-in animation when inserted*/ + + -webkit-animation: fadeOut 800ms; + -moz-animation: fadeOut 800ms; + animation: fadeOut 800ms; + -webkit-animation: fadeIn 800ms; + -moz-animation: fadeIn 800ms; + animation: fadeIn 800ms; + vertical-align: middle; + background-color: #f7f7f7; + overflow: visible; + border: #bbbbbb 1px solid; + outline: none; + padding: 3px; + margin: 0px; + padding-left: 7px; + font-family: monospace; + min-height: 50px; + position: absolute; +} +.pretooltiparrow { + left: 0px; + margin: 0px; + top: -16px; + width: 40px; + height: 16px; + overflow: hidden; + position: absolute; +} +.pretooltiparrow:before { + background-color: #f7f7f7; + border: 1px #bbbbbb solid; + z-index: 11; + content: ""; + position: absolute; + left: 15px; + top: 10px; + width: 25px; + height: 25px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); +} diff --git a/IPython/frontend/html/notebook/static/js/cell.js b/IPython/frontend/html/notebook/static/js/cell.js index 613dd9fb7..68c876976 100644 --- a/IPython/frontend/html/notebook/static/js/cell.js +++ b/IPython/frontend/html/notebook/static/js/cell.js @@ -14,13 +14,9 @@ var IPython = (function (IPython) { var utils = IPython.utils; - var Cell = function (notebook) { + var Cell = function () { this.placeholder = this.placeholder || ''; - this.notebook = notebook; this.read_only = false; - if (notebook){ - this.read_only = notebook.read_only; - } this.selected = false; this.element = null; this.create_element(); @@ -38,15 +34,15 @@ var IPython = (function (IPython) { Cell.prototype.bind_events = function () { var that = this; - var nb = that.notebook; + // We trigger events so that Cell doesn't have to depend on Notebook. that.element.click(function (event) { if (that.selected === false) { - nb.select(nb.find_cell_index(that)); + $([IPython.events]).trigger('select.Cell', {'cell':that}); } }); that.element.focusin(function (event) { if (that.selected === false) { - nb.select(nb.find_cell_index(that)); + $([IPython.events]).trigger('select.Cell', {'cell':that}); } }); }; diff --git a/IPython/frontend/html/notebook/static/js/codecell.js b/IPython/frontend/html/notebook/static/js/codecell.js index 5119461d8..ab157a265 100644 --- a/IPython/frontend/html/notebook/static/js/codecell.js +++ b/IPython/frontend/html/notebook/static/js/codecell.js @@ -10,18 +10,18 @@ //============================================================================ var IPython = (function (IPython) { + "use strict"; var utils = IPython.utils; + var key = IPython.utils.keycodes; - var CodeCell = function (notebook) { + var CodeCell = function (kernel) { + // The kernel doesn't have to be set at creation time, in that case + // it will be null and set_kernel has to be called later. + this.kernel = kernel || null; this.code_mirror = null; this.input_prompt_number = null; - this.is_completing = false; - this.completion_cursor = null; - this.outputs = []; - this.collapsed = false; - this.tooltip_timeout = null; - this.clear_out_timeout = null; + this.tooltip_on_tab = true; IPython.Cell.apply(this, arguments); }; @@ -43,22 +43,16 @@ var IPython = (function (IPython) { onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) }); input.append(input_area); - var output = $('
').addClass('output vbox'); + var output = $('
'); cell.append(input).append(output); this.element = cell; - this.collapse(); - }; + this.output_area = new IPython.OutputArea(output, true); - //TODO, try to diminish the number of parameters. - CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){ - var that = this; - if (pre_cursor === "" || pre_cursor === "(" ) { - // don't do anything if line beggin with '(' or is empty - } else { - // Will set a timer to request tooltip in `time` - that.tooltip_timeout = setTimeout(function(){ - IPython.notebook.request_tool_tip(that, pre_cursor) - },time); + // construct a completer only if class exist + // otherwise no print view + if (IPython.Completer !== undefined) + { + this.completer = new IPython.Completer(this); } }; @@ -72,30 +66,24 @@ var IPython = (function (IPython) { return false; } - // note that we are comparing and setting the time to wait at each key press. - // a better wqy might be to generate a new function on each time change and - // assign it to CodeCell.prototype.request_tooltip_after_time - tooltip_wait_time = this.notebook.time_before_tooltip; - tooltip_on_tab = this.notebook.tooltip_on_tab; var that = this; // whatever key is pressed, first, cancel the tooltip request before - // they are sent, and remove tooltip if any - if(event.type === 'keydown' ) { - that.remove_and_cancel_tooltip(); + // they are sent, and remove tooltip if any, except for tab again + if (event.type === 'keydown' && event.which != key.TAB ) { + IPython.tooltip.remove_and_cancel_tooltip(); }; + var cur = editor.getCursor(); - if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { + if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) { // Always ignore shift-enter in CodeMirror as we handle it. return true; - } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) { - // triger aon keypress (!) otherwise inconsistent event.which depending on plateform + } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) { + // triger on keypress (!) otherwise inconsistent event.which depending on plateform // browser and keyboard layout ! // Pressing '(' , request tooltip, don't forget to reappend it - var cursor = editor.getCursor(); - var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'('; - that.request_tooltip_after_time(pre_cursor,tooltip_wait_time); - } else if (event.which === 38) { + IPython.tooltip.pending(that); + } else if (event.which === key.UPARROW && event.type === 'keydown') { // If we are not at the top, let CM handle the up arrow and // prevent the global keydown handler from handling it. if (!that.at_top()) { @@ -104,7 +92,10 @@ var IPython = (function (IPython) { } else { return true; }; - } else if (event.which === 40) { + } else if (event.which === key.ESC) { + IPython.tooltip.remove_and_cancel_tooltip(true); + return true; + } else if (event.which === key.DOWNARROW && event.type === 'keydown') { // If we are not at the bottom, let CM handle the down arrow and // prevent the global keydown handler from handling it. if (!that.at_bottom()) { @@ -113,34 +104,27 @@ var IPython = (function (IPython) { } else { return true; }; - } else if (event.keyCode === 9 && event.type == 'keydown') { + } else if (event.keyCode === key.TAB && event.type == 'keydown') { // Tab completion. - var cur = editor.getCursor(); //Do not trim here because of tooltip var pre_cursor = editor.getRange({line:cur.line,ch:0},cur); if (pre_cursor.trim() === "") { // Don't autocomplete if the part of the line before the cursor // is empty. In this case, let CodeMirror handle indentation. return false; - } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) { - that.request_tooltip_after_time(pre_cursor,0); + } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && that.tooltip_on_tab ) { + IPython.tooltip.request(that); // Prevent the event from bubbling up. event.stop(); // Prevent CodeMirror from handling the tab. return true; } else { - pre_cursor.trim(); - // Autocomplete the current line. event.stop(); - var line = editor.getLine(cur.line); - this.is_completing = true; - this.completion_cursor = cur; - IPython.notebook.complete_cell(this, line, cur.ch); + this.completer.startCompletion(); return true; }; - } else if (event.keyCode === 8 && event.type == 'keydown') { + } else if (event.keyCode === key.BACKSPACE && event.type == 'keydown') { // If backspace and the line ends with 4 spaces, remove them. - var cur = editor.getCursor(); var line = editor.getLine(cur.line); var ending = line.slice(-4); if (ending === ' ') { @@ -156,394 +140,45 @@ var IPython = (function (IPython) { } else { // keypress/keyup also trigger on TAB press, and we don't want to // use those to disable tab completion. - if (this.is_completing && event.keyCode !== 9) { - var ed_cur = editor.getCursor(); - var cc_cur = this.completion_cursor; - if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) { - this.is_completing = false; - this.completion_cursor = null; - }; - }; return false; }; return false; }; - CodeCell.prototype.remove_and_cancel_tooltip = function() { - // note that we don't handle closing directly inside the calltip - // as in the completer, because it is not focusable, so won't - // get the event. - if (this.tooltip_timeout != null){ - clearTimeout(this.tooltip_timeout); - $('#tooltip').remove(); - this.tooltip_timeout = null; - } - } - - CodeCell.prototype.finish_tooltip = function (reply) { - // Extract call tip data; the priority is call, init, main. - defstring = reply.call_def; - if (defstring == null) { defstring = reply.init_definition; } - if (defstring == null) { defstring = reply.definition; } - - docstring = reply.call_docstring; - if (docstring == null) { docstring = reply.init_docstring; } - if (docstring == null) { docstring = reply.docstring; } - if (docstring == null) { docstring = ""; } - - name=reply.name; - - var that = this; - var tooltip = $('
').attr('id', 'tooltip').addClass('tooltip'); - // remove to have the tooltip not Limited in X and Y - tooltip.addClass('smalltooltip'); - var pre=$('
').html(utils.fixConsole(docstring));
-        var expandlink=$('').attr('href',"#");
-            expandlink.addClass("ui-corner-all"); //rounded corner
-            expandlink.attr('role',"button");
-            //expandlink.addClass('ui-button');
-            //expandlink.addClass('ui-state-default');
-        var expandspan=$('').text('Expand');
-            expandspan.addClass('ui-icon');
-            expandspan.addClass('ui-icon-plus');
-        expandlink.append(expandspan);
-        expandlink.attr('id','expanbutton');
-        expandlink.click(function(){
-            tooltip.removeClass('smalltooltip');
-            tooltip.addClass('bigtooltip');
-            $('#expanbutton').remove();
-            setTimeout(function(){that.code_mirror.focus();}, 50);
-        });
-        var morelink=$('').attr('href',"#");
-            morelink.attr('role',"button");
-            morelink.addClass('ui-button');
-            //morelink.addClass("ui-corner-all"); //rounded corner
-            //morelink.addClass('ui-state-default');
-        var morespan=$('').text('Open in Pager');
-            morespan.addClass('ui-icon');
-            morespan.addClass('ui-icon-arrowstop-l-n');
-        morelink.append(morespan);
-        morelink.click(function(){
-            var msg_id = IPython.notebook.kernel.execute(name+"?");
-            IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
-            that.remove_and_cancel_tooltip();
-            setTimeout(function(){that.code_mirror.focus();}, 50);
-        });
-
-        var closelink=$('').attr('href',"#");
-            closelink.attr('role',"button");
-            closelink.addClass('ui-button');
-            //closelink.addClass("ui-corner-all"); //rounded corner
-            //closelink.adClass('ui-state-default'); // grey background and blue cross
-        var closespan=$('').text('Close');
-            closespan.addClass('ui-icon');
-            closespan.addClass('ui-icon-close');
-        closelink.append(closespan);
-        closelink.click(function(){
-            that.remove_and_cancel_tooltip();
-            setTimeout(function(){that.code_mirror.focus();}, 50);
-            });
-        //construct the tooltip
-        tooltip.append(closelink);
-        tooltip.append(expandlink);
-        tooltip.append(morelink);
-        if(defstring){
-            defstring_html = $('
').html(utils.fixConsole(defstring));
-            tooltip.append(defstring_html);
-        }
-        tooltip.append(pre);
-        var pos = this.code_mirror.cursorCoords();
-        tooltip.css('left',pos.x+'px');
-        tooltip.css('top',pos.yBot+'px');
-        $('body').append(tooltip);
-
-        // issues with cross-closing if multiple tooltip in less than 5sec
-        // keep it comented for now
-        // setTimeout(that.remove_and_cancel_tooltip, 5000);
-    };
 
-    // As you type completer
-    CodeCell.prototype.finish_completing = function (matched_text, matches) {
-        if(matched_text[0]=='%'){
-            completing_from_magic = true;
-            completing_to_magic = false;
-        } else {
-            completing_from_magic = false;
-            completing_to_magic = false;
-        }
-        //return if not completing or nothing to complete
-        if (!this.is_completing || matches.length === 0) {return;}
-
-        // for later readability
-        var key = { tab:9,
-                    esc:27,
-                    backspace:8,
-                    space:32,
-                    shift:16,
-                    enter:13,
-                    // _ is 95
-                    isCompSymbol : function (code)
-                        {
-                        return (code > 64 && code <= 90)
-                            || (code >= 97 && code <= 122)
-                            || (code == 95)
-                        },
-                    dismissAndAppend : function (code)
-                        {
-                        chararr = '()[]+-/\\. ,=*'.split("");
-                        codearr = chararr.map(function(x){return x.charCodeAt(0)});
-                        return jQuery.inArray(code, codearr) != -1;
-                        }
-
-                    }
-
-        // smart completion, sort kwarg ending with '='
-        var newm = new Array();
-        if(this.notebook.smart_completer)
-        {
-            kwargs = new Array();
-            other = new Array();
-            for(var i = 0 ; i 1 ){
-                var tem1, tem2, s, A = A.slice(0).sort();
-                tem1 = A[0];
-                s = tem1.length;
-                tem2 = A.pop();
-                while(s && tem2.indexOf(tem1) == -1){
-                    tem1 = tem1.substring(0, --s);
-                }
-                shared = tem1;
-            }
-            if (shared[0] == '%' && !completing_from_magic)
-            {
-                shared = shared.substr(1);
-                return [shared, true];
-            } else {
-                return [shared, false];
-            }
-        }
+    // Kernel related calls.
 
+    CodeCell.prototype.set_kernel = function (kernel) {
+        this.kernel = kernel;
+    }
 
-        //try to check if the user is typing tab at least twice after a word
-        // and completion is "done"
-        fallback_on_tooltip_after = 2
-        if(matches.length == 1 && matched_text === matches[0])
-        {
-            if(this.npressed >fallback_on_tooltip_after  && this.prevmatch==matched_text)
-            {
-                this.request_tooltip_after_time(matched_text+'(',0);
-                return;
-            }
-            this.prevmatch = matched_text
-            this.npressed = this.npressed+1;
-        }
-        else
-        {
-            this.prevmatch = "";
-            this.npressed = 0;
-        }
-        // end fallback on tooltip
-        //==================================
-        // Real completion logic start here
-        var that = this;
-        var cur = this.completion_cursor;
-        var done = false;
-
-        // call to dismmiss the completer
-        var close = function () {
-            if (done) return;
-            done = true;
-            if (complete != undefined)
-            {complete.remove();}
-            that.is_completing = false;
-            that.completion_cursor = null;
-        };
 
-        // update codemirror with the typed text
-        prev = matched_text
-        var update = function (inserted_text, event) {
-            that.code_mirror.replaceRange(
-                inserted_text,
-                {line: cur.line, ch: (cur.ch-matched_text.length)},
-                {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
-            );
-            prev = inserted_text
-            if(event != null){
-                event.stopPropagation();
-                event.preventDefault();
-            }
-        };
-        // insert the given text and exit the completer
-        var insert = function (selected_text, event) {
-            update(selected_text)
-            close();
-            setTimeout(function(){that.code_mirror.focus();}, 50);
-        };
-
-        // insert the curent highlited selection and exit
-        var pick = function () {
-            insert(select.val()[0],null);
+    CodeCell.prototype.execute = function () {
+        this.output_area.clear_output(true, true, true);
+        this.set_input_prompt('*');
+        this.element.addClass("running");
+        var callbacks = {
+            'execute_reply': $.proxy(this._handle_execute_reply, this),
+            'output': $.proxy(this.output_area.handle_output, this.output_area),
+            'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
+            'set_next_input': $.proxy(this._handle_set_next_input, this)
         };
+        var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
+    };
 
 
-        // Define function to clear the completer, refill it with the new
-        // matches, update the pseuso typing field. autopick insert match if
-        // only one left, in no matches (anymore) dismiss itself by pasting
-        // what the user have typed until then
-        var complete_with = function(matches,typed_text,autopick,event)
-        {
-            // If autopick an only one match, past.
-            // Used to 'pick' when pressing tab
-            var prefix = '';
-            if(completing_to_magic && !completing_from_magic)
-            {
-                prefix='%';
-            }
-            if (matches.length < 1) {
-                insert(prefix+typed_text,event);
-                if(event != null){
-                event.stopPropagation();
-                event.preventDefault();
-                }
-            } else if (autopick && matches.length == 1) {
-                insert(matches[0],event);
-                if(event != null){
-                event.stopPropagation();
-                event.preventDefault();
-                }
-                return;
-            }
-            //clear the previous completion if any
-            update(prefix+typed_text,event);
-            complete.children().children().remove();
-            $('#asyoutype').html(""+prefix+matched_text+""+typed_text.substr(matched_text.length));
-            select = $('#asyoutypeselect');
-            for (var i = 0; i').html(matches[i]));
-            }
-            select.children().first().attr('selected','true');
-        }
+    CodeCell.prototype._handle_execute_reply = function (content) {
+        this.set_input_prompt(content.execution_count);
+        this.element.removeClass("running");
+        $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
+    }
 
-        // create html for completer
-        var complete = $('
').addClass('completions'); - complete.attr('id','complete'); - complete.append($('

').attr('id', 'asyoutype').html('fixed partuser part'));//pseudo input field - - var select = $('').attr('multiple', 'true').attr('size', Math.min(10, this.raw_result.length)); + var pos = this.editor.cursorCoords(); + + // TODO: I propose to remove enough horizontal pixel + // to align the text later + this.complete.css('left', pos.x + 'px'); + this.complete.css('top', pos.yBot + 'px'); + this.complete.append(this.sel); + + $('body').append(this.complete); + //build the container + var that = this; + this.sel.dblclick(function () { + that.pick(); + }); + this.sel.blur(this.close); + this.sel.keydown(function (event) { + that.keydown(event); + }); + + this.build_gui_list(this.raw_result); + + this.sel.focus(); + // Opera sometimes ignores focusing a freshly created node + if (window.opera) setTimeout(function () { + if (!this.done) this.sel.focus(); + }, 100); + return true; + } + + Completer.prototype.insert = function (completion) { + this.editor.replaceRange(completion.str, completion.from, completion.to); + } + + Completer.prototype.build_gui_list = function (completions) { + // Need to clear the all list + for (var i = 0; i < completions.length; ++i) { + var opt = $('

'); - dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.'); - $(document).append(dialog); - dialog.dialog({ - resizable: false, - modal: true, - title: "Dead kernel", - buttons : { - "Restart": function () { - that.start_kernel(); + that.kernel.restart(); $(this).dialog('close'); }, "Continue running": function () { @@ -1040,57 +926,6 @@ var IPython = (function (IPython) { }; - Notebook.prototype.handle_output = function (cell, msg_type, content) { - var json = {}; - json.output_type = msg_type; - if (msg_type === "stream") { - json.text = content.data; - json.stream = content.name; - } else if (msg_type === "display_data") { - json = this.convert_mime_types(json, content.data); - } else if (msg_type === "pyout") { - json.prompt_number = content.execution_count; - json = this.convert_mime_types(json, content.data); - } else if (msg_type === "pyerr") { - json.ename = content.ename; - json.evalue = content.evalue; - json.traceback = content.traceback; - }; - // append with dynamic=true - cell.append_output(json, true); - this.dirty = true; - }; - - - Notebook.prototype.convert_mime_types = function (json, data) { - if (data['text/plain'] !== undefined) { - json.text = data['text/plain']; - }; - if (data['text/html'] !== undefined) { - json.html = data['text/html']; - }; - if (data['image/svg+xml'] !== undefined) { - json.svg = data['image/svg+xml']; - }; - if (data['image/png'] !== undefined) { - json.png = data['image/png']; - }; - if (data['image/jpeg'] !== undefined) { - json.jpeg = data['image/jpeg']; - }; - if (data['text/latex'] !== undefined) { - json.latex = data['text/latex']; - }; - if (data['application/json'] !== undefined) { - json.json = data['application/json']; - }; - if (data['application/javascript'] !== undefined) { - json.javascript = data['application/javascript']; - } - return json; - }; - - Notebook.prototype.execute_selected_cell = function (options) { // add_new: should a new cell be added if we are at the end of the nb // terminal: execute in terminal mode, which stays in the current cell @@ -1100,12 +935,7 @@ var IPython = (function (IPython) { var cell = that.get_selected_cell(); var cell_index = that.find_cell_index(cell); if (cell instanceof IPython.CodeCell) { - cell.clear_output(true, true, true); - cell.set_input_prompt('*'); - cell.element.addClass("running"); - var code = cell.get_text(); - var msg_id = that.kernel.execute(cell.get_text()); - that.msg_cell_map[msg_id] = cell.cell_id; + cell.execute(); } else if (cell instanceof IPython.HTMLCell) { cell.render(); } @@ -1133,37 +963,6 @@ var IPython = (function (IPython) { this.scroll_to_bottom(); }; - - Notebook.prototype.request_tool_tip = function (cell,func) { - // Feel free to shorten this logic if you are better - // than me in regEx - // basicaly you shoul be able to get xxx.xxx.xxx from - // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2, - // remove everything between matchin bracket (need to iterate) - matchBracket = /\([^\(\)]+\)/g; - oldfunc = func; - func = func.replace(matchBracket,""); - while( oldfunc != func ) - { - oldfunc = func; - func = func.replace(matchBracket,""); - } - // remove everythin after last open bracket - endBracket = /\([^\(]*$/g; - func = func.replace(endBracket,""); - var re = /[a-z_][0-9a-z._]+$/gi; // casse insensitive - var msg_id = this.kernel.object_info_request(re.exec(func)); - if(typeof(msg_id)!='undefined'){ - this.msg_cell_map[msg_id] = cell.cell_id; - } - }; - - Notebook.prototype.complete_cell = function (cell, line, cursor_pos) { - var msg_id = this.kernel.complete(line, cursor_pos); - this.msg_cell_map[msg_id] = cell.cell_id; - }; - - // Persistance and loading Notebook.prototype.get_notebook_id = function () { @@ -1226,11 +1025,11 @@ var IPython = (function (IPython) { Notebook.prototype.toJSON = function () { var cells = this.get_cells(); var ncells = cells.length; - cell_array = new Array(ncells); + var cell_array = new Array(ncells); for (var i=0; i'); + dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.'); + $(document).append(dialog); + dialog.dialog({ + resizable: false, + modal: true, + title: "Dead kernel", + buttons : { + "Restart": function () { + $([IPython.events]).trigger('status_restarting.Kernel'); + IPython.notebook.start_kernel(); + $(this).dialog('close'); + }, + "Continue running": function () { + $(this).dialog('close'); + } + } + }); + }); // Notebook events $([IPython.events]).on('notebook_loading.Notebook', function () { that.set_message("Loading notebook",500); diff --git a/IPython/frontend/html/notebook/static/js/outputarea.js b/IPython/frontend/html/notebook/static/js/outputarea.js new file mode 100644 index 000000000..459ac418b --- /dev/null +++ b/IPython/frontend/html/notebook/static/js/outputarea.js @@ -0,0 +1,403 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2008-2011 The IPython Development Team +// +// Distributed under the terms of the BSD License. The full license is in +// the file COPYING, distributed as part of this software. +//---------------------------------------------------------------------------- + +//============================================================================ +// OutputArea +//============================================================================ + +var IPython = (function (IPython) { + "use strict"; + + var utils = IPython.utils; + + var OutputArea = function (selector, prompt_area) { + this.selector = selector; + this.element = $(selector); + this.outputs = []; + this.collapsed = false; + this.clear_out_timeout = null; + if (prompt_area === undefined) { + this.prompt_area = true; + } else { + this.prompt_area = prompt_area; + }; + this.style(); + }; + + + OutputArea.prototype.style = function () { + this.element.addClass('output vbox'); + this.collapse(); + }; + + + OutputArea.prototype.collapse = function () { + if (!this.collapsed) { + this.element.hide(); + this.collapsed = true; + }; + }; + + + OutputArea.prototype.expand = function () { + if (this.collapsed) { + this.element.show(); + this.collapsed = false; + }; + }; + + + OutputArea.prototype.toggle_output = function () { + if (this.collapsed) { + this.expand(); + } else { + this.collapse(); + }; + }; + + + // typeset with MathJax if MathJax is available + OutputArea.prototype.typeset = function () { + if (window.MathJax){ + MathJax.Hub.Queue(["Typeset",MathJax.Hub]); + } + }; + + + OutputArea.prototype.handle_output = function (msg_type, content) { + var json = {}; + json.output_type = msg_type; + if (msg_type === "stream") { + json.text = content.data; + json.stream = content.name; + } else if (msg_type === "display_data") { + json = this.convert_mime_types(json, content.data); + } else if (msg_type === "pyout") { + json.prompt_number = content.execution_count; + json = this.convert_mime_types(json, content.data); + } else if (msg_type === "pyerr") { + json.ename = content.ename; + json.evalue = content.evalue; + json.traceback = content.traceback; + }; + // append with dynamic=true + this.append_output(json, true); + }; + + + OutputArea.prototype.convert_mime_types = function (json, data) { + if (data['text/plain'] !== undefined) { + json.text = data['text/plain']; + }; + if (data['text/html'] !== undefined) { + json.html = data['text/html']; + }; + if (data['image/svg+xml'] !== undefined) { + json.svg = data['image/svg+xml']; + }; + if (data['image/png'] !== undefined) { + json.png = data['image/png']; + }; + if (data['image/jpeg'] !== undefined) { + json.jpeg = data['image/jpeg']; + }; + if (data['text/latex'] !== undefined) { + json.latex = data['text/latex']; + }; + if (data['application/json'] !== undefined) { + json.json = data['application/json']; + }; + if (data['application/javascript'] !== undefined) { + json.javascript = data['application/javascript']; + } + return json; + }; + + + OutputArea.prototype.append_output = function (json, dynamic) { + // If dynamic is true, javascript output will be eval'd. + this.expand(); + this.flush_clear_timeout(); + if (json.output_type === 'pyout') { + this.append_pyout(json, dynamic); + } else if (json.output_type === 'pyerr') { + this.append_pyerr(json); + } else if (json.output_type === 'display_data') { + this.append_display_data(json, dynamic); + } else if (json.output_type === 'stream') { + this.append_stream(json); + }; + this.outputs.push(json); + }; + + + OutputArea.prototype.create_output_area = function () { + var oa = $("
").addClass("hbox output_area"); + if (this.prompt_area) { + oa.append($('
').addClass('prompt')); + } + return oa; + }; + + + OutputArea.prototype.append_pyout = function (json, dynamic) { + var n = json.prompt_number || ' '; + var toinsert = this.create_output_area(); + if (this.prompt_area) { + toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:'); + } + this.append_mime_type(json, toinsert, dynamic); + this.element.append(toinsert); + // If we just output latex, typeset it. + if ((json.latex !== undefined) || (json.html !== undefined)) { + this.typeset(); + }; + }; + + + OutputArea.prototype.append_pyerr = function (json) { + var tb = json.traceback; + if (tb !== undefined && tb.length > 0) { + var s = ''; + var len = tb.length; + for (var i=0; i 0){ + // have at least one output to consider + var last = this.outputs[this.outputs.length-1]; + if (last.output_type == 'stream' && json.stream == last.stream){ + // latest output was in the same stream, + // so append directly into its pre tag + // escape ANSI & HTML specials: + var text = utils.fixConsole(json.text); + this.element.find('div.'+subclass).last().find('pre').append(text); + return; + } + } + + // If we got here, attach a new div + var toinsert = this.create_output_area(); + this.append_text(json.text, toinsert, "output_stream "+subclass); + this.element.append(toinsert); + }; + + + OutputArea.prototype.append_display_data = function (json, dynamic) { + var toinsert = this.create_output_area(); + this.append_mime_type(json, toinsert, dynamic); + this.element.append(toinsert); + // If we just output latex, typeset it. + if ( (json.latex !== undefined) || (json.html !== undefined) ) { + this.typeset(); + }; + }; + + + OutputArea.prototype.append_mime_type = function (json, element, dynamic) { + if (json.javascript !== undefined && dynamic) { + this.append_javascript(json.javascript, element, dynamic); + } else if (json.html !== undefined) { + this.append_html(json.html, element); + } else if (json.latex !== undefined) { + this.append_latex(json.latex, element); + } else if (json.svg !== undefined) { + this.append_svg(json.svg, element); + } else if (json.png !== undefined) { + this.append_png(json.png, element); + } else if (json.jpeg !== undefined) { + this.append_jpeg(json.jpeg, element); + } else if (json.text !== undefined) { + this.append_text(json.text, element); + }; + }; + + + OutputArea.prototype.append_html = function (html, element) { + var toinsert = $("
").addClass("box-flex1 output_subarea output_html rendered_html"); + toinsert.append(html); + element.append(toinsert); + }; + + + OutputArea.prototype.append_javascript = function (js, container) { + // We just eval the JS code, element appears in the local scope. + var element = $("
").addClass("box-flex1 output_subarea"); + container.append(element); + // Div for js shouldn't be drawn, as it will add empty height to the area. + container.hide(); + // If the Javascript appends content to `element` that should be drawn, then + // it must also call `container.show()`. + eval(js); + } + + + OutputArea.prototype.append_text = function (data, element, extra_class) { + var toinsert = $("
").addClass("box-flex1 output_subarea output_text"); + // escape ANSI & HTML specials in plaintext: + data = utils.fixConsole(data); + if (extra_class){ + toinsert.addClass(extra_class); + } + toinsert.append($("
").html(data));
+        element.append(toinsert);
+    };
+
+
+    OutputArea.prototype.append_svg = function (svg, element) {
+        var toinsert = $("
").addClass("box-flex1 output_subarea output_svg"); + toinsert.append(svg); + element.append(toinsert); + }; + + + OutputArea.prototype.append_png = function (png, element) { + var toinsert = $("
").addClass("box-flex1 output_subarea output_png"); + toinsert.append($("").attr('src','data:image/png;base64,'+png)); + element.append(toinsert); + }; + + + OutputArea.prototype.append_jpeg = function (jpeg, element) { + var toinsert = $("
").addClass("box-flex1 output_subarea output_jpeg"); + toinsert.append($("").attr('src','data:image/jpeg;base64,'+jpeg)); + element.append(toinsert); + }; + + + OutputArea.prototype.append_latex = function (latex, element) { + // This method cannot do the typesetting because the latex first has to + // be on the page. + var toinsert = $("
").addClass("box-flex1 output_subarea output_latex"); + toinsert.append(latex); + element.append(toinsert); + }; + + + OutputArea.prototype.handle_clear_output = function (content) { + this.clear_output(content.stdout, content.stderr, content.other); + } + + + OutputArea.prototype.clear_output = function (stdout, stderr, other) { + var that = this; + if (this.clear_out_timeout != null){ + // fire previous pending clear *immediately* + clearTimeout(this.clear_out_timeout); + this.clear_out_timeout = null; + this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other); + } + // store flags for flushing the timeout + this._clear_stdout = stdout; + this._clear_stderr = stderr; + this._clear_other = other; + this.clear_out_timeout = setTimeout(function() { + // really clear timeout only after a short delay + // this reduces flicker in 'clear_output; print' cases + that.clear_out_timeout = null; + that._clear_stdout = that._clear_stderr = that._clear_other = null; + that.clear_output_callback(stdout, stderr, other); + }, 500 + ); + }; + + + OutputArea.prototype.clear_output_callback = function (stdout, stderr, other) { + var output_div = this.element; + + if (stdout && stderr && other){ + // clear all, no need for logic + output_div.html(""); + this.outputs = []; + return; + } + // remove html output + // each output_subarea that has an identifying class is in an output_area + // which is the element to be removed. + if (stdout) { + output_div.find("div.output_stdout").parent().remove(); + } + if (stderr) { + output_div.find("div.output_stderr").parent().remove(); + } + if (other) { + output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove(); + } + + // remove cleared outputs from JSON list: + for (var i = this.outputs.length - 1; i >= 0; i--) { + var out = this.outputs[i]; + var output_type = out.output_type; + if (output_type == "display_data" && other) { + this.outputs.splice(i,1); + } else if (output_type == "stream") { + if (stdout && out.stream == "stdout") { + this.outputs.splice(i,1); + } else if (stderr && out.stream == "stderr") { + this.outputs.splice(i,1); + } + } + } + }; + + + OutputArea.prototype.flush_clear_timeout = function() { + var output_div = this.element; + if (this.clear_out_timeout){ + clearTimeout(this.clear_out_timeout); + this.clear_out_timeout = null; + this.clear_output_callback(this._clear_stdout, this._clear_stderr, this._clear_other); + }; + } + + + // JSON serialization + + OutputArea.prototype.fromJSON = function (outputs) { + var len = outputs.length; + for (var i=0; i').addClass('tooltipbuttons'); + + // will contain the docstring + this.text = $('
').addClass('tooltiptext').addClass('smalltooltip'); + + // build the buttons menu on the upper right + // expand the tooltip to see more + var expandlink = $('').attr('href', "#").addClass("ui-corner-all") //rounded corner + .attr('role', "button").attr('id', 'expanbutton').attr('title', 'Grow the tooltip vertically (press tab 2 times)').click(function () { + that.expand() + }).append( + $('').text('Expand').addClass('ui-icon').addClass('ui-icon-plus')); + + // open in pager + var morelink = $('').attr('href', "#").attr('role', "button").addClass('ui-button').attr('title', 'show the current docstring in pager (press tab 4 times)'); + var morespan = $('').text('Open in Pager').addClass('ui-icon').addClass('ui-icon-arrowstop-l-n'); + morelink.append(morespan); + morelink.click(function () { + that.showInPager(); + }); + + // close the tooltip + var closelink = $('').attr('href', "#").attr('role', "button").addClass('ui-button'); + var closespan = $('').text('Close').addClass('ui-icon').addClass('ui-icon-close'); + closelink.append(closespan); + closelink.click(function () { + that.remove_and_cancel_tooltip(true); + }); + + this._clocklink = $('').attr('href', "#"); + this._clocklink.attr('role', "button"); + this._clocklink.addClass('ui-button'); + this._clocklink.attr('title', 'Tootip is not dismissed while typing for 10 seconds'); + var clockspan = $('').text('Close'); + clockspan.addClass('ui-icon'); + clockspan.addClass('ui-icon-clock'); + this._clocklink.append(clockspan); + this._clocklink.click(function () { + that.cancel_stick(); + }); + + + + + //construct the tooltip + // add in the reverse order you want them to appear + this.buttons.append(closelink); + this.buttons.append(expandlink); + this.buttons.append(morelink); + this.buttons.append(this._clocklink); + this._clocklink.hide(); + + + // we need a phony element to make the small arrow + // of the tooltip in css + // we will move the arrow later + this.arrow = $('
').addClass('pretooltiparrow'); + this.tooltip.append(this.buttons); + this.tooltip.append(this.arrow); + this.tooltip.append(this.text); + + // function that will be called if you press tab 1, 2, 3... times in a row + this.tabs_functions = [function (cell, text) { + that._request_tooltip(cell, text); + IPython.notification_widget.set_message('tab again to expand pager', 2500); + }, function () { + that.expand(); + IPython.notification_widget.set_message('tab again to make pager sticky for 10s', 2500); + }, function () { + that.stick(); + IPython.notification_widget.set_message('tab again to open help in pager', 2500); + }, function (cell) { + that.cancel_stick(); + that.showInPager(cell); + that._cmfocus(); + }]; + // call after all the tabs function above have bee call to clean their effects + // if necessary + this.reset_tabs_function = function (cell, text) { + this._old_cell = (cell) ? cell : null; + this._old_request = (text) ? text : null; + this._consecutive_counter = 0; + } + }; + + Tooltip.prototype.showInPager = function (cell) { + // reexecute last call in pager by appending ? to show back in pager + var that = this; + var empty = function () {}; + IPython.notebook.kernel.execute( + that.name + '?', { + 'execute_reply': empty, + 'output': empty, + 'clear_output': empty, + 'cell': cell + }, { + 'silent': false + }); + this.remove_and_cancel_tooltip(); + this._cmfocus(); + } + + // grow the tooltip verticaly + Tooltip.prototype.expand = function () { + this.text.removeClass('smalltooltip'); + this.text.addClass('bigtooltip'); + $('#expanbutton').hide('slow'); + this._cmfocus(); + } + + // deal with all the logic of hiding the tooltip + // and reset it's status + Tooltip.prototype._hide = function () { + this.tooltip.fadeOut('fast'); + $('#expanbutton').show('slow'); + this.text.removeClass('bigtooltip'); + this.text.addClass('smalltooltip'); + // keep scroll top to be sure to always see the first line + this.text.scrollTop(0); + this._hidden = true; + this.code_mirror = null; + } + + Tooltip.prototype.remove_and_cancel_tooltip = function (force) { + // note that we don't handle closing directly inside the calltip + // as in the completer, because it is not focusable, so won't + // get the event. + if (this._sticky == false || force == true) { + this.cancel_stick(); + this._hide(); + } + this.cancel_pending(); + this.reset_tabs_function(); + this._cmfocus(); + } + + // cancel autocall done after '(' for example. + Tooltip.prototype.cancel_pending = function () { + if (this._tooltip_timeout != null) { + clearTimeout(this._tooltip_timeout); + this._tooltip_timeout = null; + } + } + + // will trigger tooltip after timeout + Tooltip.prototype.pending = function (cell) { + var that = this; + this._tooltip_timeout = setTimeout(function () { + that.request(cell) + }, that.time_before_tooltip); + } + + Tooltip.prototype._request_tooltip = function (cell, func) { + // use internally just to make the request to the kernel + // Feel free to shorten this logic if you are better + // than me in regEx + // basicaly you shoul be able to get xxx.xxx.xxx from + // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2, + // remove everything between matchin bracket (need to iterate) + var matchBracket = /\([^\(\)]+\)/g; + var endBracket = /\([^\(]*$/g; + var oldfunc = func; + + func = func.replace(matchBracket, ""); + while (oldfunc != func) { + oldfunc = func; + func = func.replace(matchBracket, ""); + } + // remove everything after last open bracket + func = func.replace(endBracket, ""); + + var re = /[a-z_][0-9a-z._]+$/gi; // casse insensitive + var callbacks = { + 'object_info_reply': $.proxy(this._show, this) + } + var msg_id = IPython.notebook.kernel.object_info_request(re.exec(func), callbacks); + } + + // make an imediate completion request + Tooltip.prototype.request = function (cell) { + // request(codecell) + // Deal with extracting the text from the cell and counting + // call in a row + this.cancel_pending(); + var editor = cell.code_mirror; + var cursor = editor.getCursor(); + var text = editor.getRange({ + line: cursor.line, + ch: 0 + }, cursor).trim(); + + // need a permanent handel to code_mirror for future auto recall + this.code_mirror = editor; + + // now we treat the different number of keypress + // first if same cell, same text, increment counter by 1 + if (this._old_cell == cell && this._old_request == text && this._hidden == false) { + this._consecutive_counter++; + } else { + // else reset + this.cancel_stick(); + this.reset_tabs_function (cell, text); + } + + // don't do anything if line beggin with '(' or is empty + if (text === "" || text === "(") { + return; + } + + this.tabs_functions[this._consecutive_counter](cell, text); + + // then if we are at the end of list function, reset + if (this._consecutive_counter == this.tabs_functions.length) this.reset_tabs_function (cell, text); + + return; + } + + // cancel the option of having the tooltip to stick + Tooltip.prototype.cancel_stick = function () { + clearTimeout(this._stick_timeout); + this._stick_timeout = null; + this._clocklink.hide('slow'); + this._sticky = false; + } + + // put the tooltip in a sicky state for 10 seconds + // it won't be removed by remove_and_cancell() unless you called with + // the first parameter set to true. + // remove_and_cancell_tooltip(true) + Tooltip.prototype.stick = function (time) { + time = (time != undefined) ? time : 10; + var that = this; + this._sticky = true; + this._clocklink.show('slow'); + this._stick_timeout = setTimeout(function () { + that._sticky = false; + that._clocklink.hide('slow'); + }, time * 1000); + } + + // should be called with the kernel reply to actually show the tooltip + Tooltip.prototype._show = function (reply) { + // move the bubble if it is not hidden + // otherwise fade it + this.name = reply.name; + + // do some math to have the tooltip arrow on more or less on left or right + // width of the editor + var w = $(this.code_mirror.getScrollerElement()).width(); + // ofset of the editor + var o = $(this.code_mirror.getScrollerElement()).offset(); + var pos = this.code_mirror.cursorCoords(); + var xinit = pos.x; + var xinter = o.left + (xinit - o.left) / w * (w - 450); + var posarrowleft = xinit - xinter; + + + if (this._hidden == false) { + this.tooltip.animate({ + 'left': xinter - 30 + 'px', + 'top': (pos.yBot + 10) + 'px' + }); + } else { + this.tooltip.css({ + 'left': xinter - 30 + 'px' + }); + this.tooltip.css({ + 'top': (pos.yBot + 10) + 'px' + }); + } + this.arrow.animate({ + 'left': posarrowleft + 'px' + }); + this.tooltip.fadeIn('fast'); + this._hidden = false; + + // build docstring + var defstring = reply.call_def; + if (defstring == null) { + defstring = reply.init_definition; + } + if (defstring == null) { + defstring = reply.definition; + } + + var docstring = reply.call_docstring; + if (docstring == null) { + docstring = reply.init_docstring; + } + if (docstring == null) { + docstring = reply.docstring; + } + if (docstring == null) { + docstring = ""; + } + + this.text.children().remove(); + + var pre = $('
').html(utils.fixConsole(docstring));
+        if (defstring) {
+            var defstring_html = $('
').html(utils.fixConsole(defstring));
+            this.text.append(defstring_html);
+        }
+        this.text.append(pre);
+        // keep scroll top to be sure to always see the first line
+        this.text.scrollTop(0);
+    }
+
+    // convenient funciton to have the correct code_mirror back into focus
+    Tooltip.prototype._cmfocus = function () {
+        var cm = this.code_mirror;
+        if (cm == IPython.notebook.get_selected_cell())
+        {
+            setTimeout(function () {
+                cm.focus();
+            }, 50);
+        }
+    }
+
+    IPython.Tooltip = Tooltip;
+
+    return IPython;
+
+}(IPython));
diff --git a/IPython/frontend/html/notebook/static/js/utils.js b/IPython/frontend/html/notebook/static/js/utils.js
index 5b1022b7b..4467fb985 100644
--- a/IPython/frontend/html/notebook/static/js/utils.js
+++ b/IPython/frontend/html/notebook/static/js/utils.js
@@ -91,11 +91,38 @@ IPython.utils = (function (IPython) {
         }
     };
 
+    // some keycodes that seem to be platform/browser independant
+    var keycodes ={
+                BACKSPACE:  8,
+                TAB      :  9,
+                ENTER    : 13,
+                SHIFT    : 16,
+                CTRL     : 17,
+                CONTROL  : 17,
+                ALT      : 18,
+                ESC      : 27,
+                SPACE    : 32,
+                PGUP     : 33,
+                PGDOWN   : 34,
+                LEFT_ARROW: 37,
+                LEFTARROW: 37,
+                LEFT     : 37,
+                UP_ARROW : 38,
+                UPARROW  : 38,
+                UP       : 38,
+                RIGHT_ARROW:39,
+                RIGHTARROW:39,
+                RIGHT    : 39,
+                DOWN_ARROW: 40,
+                DOWNARROW: 40,
+                DOWN     : 40,
+    };
 
     return {
         uuid : uuid,
         fixConsole : fixConsole,
-        grow : grow
+        keycodes : keycodes,
+        grow : grow,
     };
 
 }(IPython));
diff --git a/IPython/frontend/html/notebook/templates/notebook.html b/IPython/frontend/html/notebook/templates/notebook.html
index 89d2ab704..64da27cab 100644
--- a/IPython/frontend/html/notebook/templates/notebook.html
+++ b/IPython/frontend/html/notebook/templates/notebook.html
@@ -1,5 +1,4 @@
 {% extends page.html %}
-
 {% block stylesheet %}
 
 {% if mathjax_url %}
@@ -17,6 +16,7 @@ window.mathjax_url = "{{mathjax_url}}";
 
 
 
+
 
 
 {% end %}
@@ -194,6 +194,8 @@ data-notebook-id={{notebook_id}}
     
+ + {% end %} @@ -218,8 +220,10 @@ data-notebook-id={{notebook_id}} + + @@ -229,7 +233,9 @@ data-notebook-id={{notebook_id}} + -{% end %} + +{% end %} diff --git a/docs/examples/widgets/directview/directview.ipynb b/docs/examples/widgets/directview/directview.ipynb new file mode 100644 index 000000000..fcd58ec2b --- /dev/null +++ b/docs/examples/widgets/directview/directview.ipynb @@ -0,0 +1,38 @@ +{ + "metadata": { + "name": "directview" + }, + "nbformat": 3, + "worksheets": [ + { + "cells": [ + { + "cell_type": "code", + "input": [ + "from directview import interact", + "from IPython.parallel import Client" + ], + "language": "python", + "outputs": [] + }, + { + "cell_type": "code", + "input": [ + "c = Client()", + "dv = c[:]" + ], + "language": "python", + "outputs": [] + }, + { + "cell_type": "code", + "input": [ + "interact(dv)" + ], + "language": "python", + "outputs": [] + } + ] + } + ] +} \ No newline at end of file