Major refactoring of the Notebook, Kernel and CodeCell JavaScript.

* Kernel doesn't depend on Notebook or CodeCell.
* CodeCell doesn't depend on Notebook, only Kernel.
* All of the kernel management logic has been moved out of the
  Notebook into the Kernel.
* Public methods of the Kernel (execute, complete, etc) take
  a callbacks object that registers the callbacks for that msg.

(rebased, cherrypicked, by Bussonnier Matthias <bussonniermatthias@gmail.com>)
(and tabs removed)

Conflicts:

	IPython/frontend/html/notebook/static/js/codecell.js
	IPython/frontend/html/notebook/static/js/completer.js
	IPython/frontend/html/notebook/static/js/tooltip.js
Brian Granger 14 years ago
parent 94d8683400
commit 0d6a698688

@ -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});
}
});
};

@ -15,12 +15,14 @@ var IPython = (function (IPython) {
var utils = IPython.utils;
var key = IPython.utils.keycodes;
var CodeCell = function (notebook) {
var CodeCell = function (kernel) {
this.kernel = kernel;
this.code_mirror = null;
this.input_prompt_number = null;
this.outputs = [];
this.collapsed = false;
this.clear_out_timeout = null;
this.tooltip_on_tab = true;
IPython.Cell.apply(this, arguments);
};
@ -65,7 +67,6 @@ var IPython = (function (IPython) {
return false;
}
var 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, except for tab again
@ -73,11 +74,12 @@ var IPython = (function (IPython) {
IPython.tooltip.remove_and_cancel_tooltip();
};
var cur = editor.getCursor();
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' && this.notebook.time_before_tooltip >= 0) {
} 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
@ -102,14 +104,13 @@ var IPython = (function (IPython) {
};
} 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 ) {
} 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();
@ -122,7 +123,6 @@ var IPython = (function (IPython) {
};
} 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 === ' ') {
@ -147,13 +147,56 @@ var IPython = (function (IPython) {
IPython.tooltip.show(reply, this);
};
// Kernel related calls.
// called when completion came back from the kernel. just forward
CodeCell.prototype.finish_completing = function (matched_text, matches) {
this.completer.finish_completing(matched_text,matches);
CodeCell.prototype.execute = function () {
this.clear_output(true, true, true);
this.set_input_prompt('*');
this.element.addClass("running");
var code = this.get_text();
var callbacks = {
'execute_reply': $.proxy(this._handle_execute_reply, this),
'output': $.proxy(this._handle_output, this),
'clear_output': $.proxy(this._handle_clear_output, this),
'cell': this
};
var msg_id = this.kernel.execute(this.get_text(), callbacks);
};
CodeCell.prototype._handle_execute_reply = function (content) {
this.set_input_prompt(content.execution_count);
this.element.removeClass("running");
// this.dirty = true;
}
CodeCell.prototype.request_tooltip = function (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)
var matchBracket = /\([^\(\)]+\)/g;
var endBracket = /\([^\(]*$/g;
var oldfunc = func;
func = func.replace(matchBracket,"");
while( oldfunc != func )
{
oldfunc = func;
func = func.replace(matchBracket,"");
}
// remove everythin 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.finish_tooltip,this)}
var msg_id = this.kernel.object_info_request(re.exec(func), callbacks);
};
// Basic cell manipulation.
CodeCell.prototype.select = function () {
IPython.Cell.prototype.select.apply(this);
this.code_mirror.refresh();
@ -173,6 +216,125 @@ var IPython = (function (IPython) {
};
CodeCell.prototype.collapse = function () {
if (!this.collapsed) {
this.element.find('div.output').hide();
this.collapsed = true;
};
};
CodeCell.prototype.expand = function () {
if (this.collapsed) {
this.element.find('div.output').show();
this.collapsed = false;
};
};
CodeCell.prototype.toggle_output = function () {
if (this.collapsed) {
this.expand();
} else {
this.collapse();
};
};
CodeCell.prototype.set_input_prompt = function (number) {
this.input_prompt_number = number;
var ns = number || "&nbsp;";
this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
};
CodeCell.prototype.clear_input = function () {
this.code_mirror.setValue('');
};
CodeCell.prototype.get_text = function () {
return this.code_mirror.getValue();
};
CodeCell.prototype.set_text = function (code) {
return this.code_mirror.setValue(code);
};
CodeCell.prototype.at_top = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === 0) {
return true;
} else {
return false;
}
};
CodeCell.prototype.at_bottom = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === (this.code_mirror.lineCount()-1)) {
return true;
} else {
return false;
}
};
// Output handling.
CodeCell.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);
};
CodeCell.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;
};
CodeCell.prototype.append_output = function (json, dynamic) {
// If dynamic is true, javascript output will be eval'd.
this.expand();
@ -298,6 +460,7 @@ var IPython = (function (IPython) {
CodeCell.prototype.append_javascript = function (js, container) {
// We just eval the JS code, element appears in the local scope.
var element = $("<div/>").addClass("box_flex1 output_subarea");
var kernel = this.kernel;
container.append(element);
// Div for js shouldn't be drawn, as it will add empty height to the area.
container.hide();
@ -349,6 +512,11 @@ var IPython = (function (IPython) {
};
CodeCell.prototype._handle_clear_output = function (content) {
this.clear_output(content.stdout, content.stderr, content.other);
}
CodeCell.prototype.clear_output = function (stdout, stderr, other) {
var that = this;
if (this.clear_out_timeout != null){
@ -361,7 +529,7 @@ var IPython = (function (IPython) {
this._clear_stdout = stdout;
this._clear_stderr = stderr;
this._clear_other = other;
this.clear_out_timeout = setTimeout(function(){
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;
@ -370,7 +538,8 @@ var IPython = (function (IPython) {
}, 500
);
};
CodeCell.prototype.clear_output_callback = function (stdout, stderr, other) {
var output_div = this.element.find("div.output");
@ -383,26 +552,26 @@ var IPython = (function (IPython) {
// 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){
if (stdout) {
output_div.find("div.output_stdout").parent().remove();
}
if (stderr){
if (stderr) {
output_div.find("div.output_stderr").parent().remove();
}
if (other){
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--){
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){
if (output_type == "display_data" && other) {
this.outputs.splice(i,1);
}else if (output_type == "stream"){
if (stdout && out.stream == "stdout"){
} else if (output_type == "stream") {
if (stdout && out.stream == "stdout") {
this.outputs.splice(i,1);
}else if (stderr && out.stream == "stderr"){
} else if (stderr && out.stream == "stderr") {
this.outputs.splice(i,1);
}
}
@ -410,10 +579,6 @@ var IPython = (function (IPython) {
};
CodeCell.prototype.clear_input = function () {
this.code_mirror.setValue('');
};
CodeCell.prototype.flush_clear_timeout = function() {
var output_div = this.element.find('div.output');
if (this.clear_out_timeout){
@ -424,66 +589,7 @@ var IPython = (function (IPython) {
}
CodeCell.prototype.collapse = function () {
if (!this.collapsed) {
this.element.find('div.output').hide();
this.collapsed = true;
};
};
CodeCell.prototype.expand = function () {
if (this.collapsed) {
this.element.find('div.output').show();
this.collapsed = false;
};
};
CodeCell.prototype.toggle_output = function () {
if (this.collapsed) {
this.expand();
} else {
this.collapse();
};
};
CodeCell.prototype.set_input_prompt = function (number) {
this.input_prompt_number = number;
var ns = number || "&nbsp;";
this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
};
CodeCell.prototype.get_text = function () {
return this.code_mirror.getValue();
};
CodeCell.prototype.set_text = function (code) {
return this.code_mirror.setValue(code);
};
CodeCell.prototype.at_top = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === 0) {
return true;
} else {
return false;
}
};
CodeCell.prototype.at_bottom = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === (this.code_mirror.lineCount()-1)) {
return true;
} else {
return false;
}
};
// JSON serialization
CodeCell.prototype.fromJSON = function (data) {
if (data.cell_type === 'code') {

@ -37,7 +37,6 @@ var IPython = (function(IPython ) {
var Completer = function(cell) {
this.cell = cell;
this.editor = cell.code_mirror;
// if last caractere before cursor is not in this, we stop completing
this.reg = /[0-9a-z.]/i; // casse insensitive
@ -47,7 +46,8 @@ var IPython = (function(IPython ) {
var cur = this.editor.getCursor();
var line = this.editor.getLine(cur.line);
// one could fork here and directly call finish completing if kernel is busy
IPython.notebook.complete_cell(this.cell, line, cur.ch);
var callbacks = {'complete_reply': $.proxy(this.finish_completing,this)};
IPython.notebook.kernel.complete(line, cur.ch, callbacks);
}
@ -85,9 +85,11 @@ var IPython = (function(IPython ) {
this.kernelCompletionRequest();
}
Completer.prototype.finish_completing =function (matched_text, matches) {
Completer.prototype.finish_completing =function (content) {
// let's build a function that wrap all that stuff into what is needed
// for the new completer:
var matched_text = content.matched_text;
var matches = content.matches;
var cur = this.editor.getCursor();
var results = CodeMirror.contextHint(this.editor);

@ -12,7 +12,7 @@
// Give us an object to bind all events to. This object should be created
// before all other objects so it exists when others register event handlers.
// To trigger an event handler:
// $([IPython.events]).trigger('event.Namespace);
// $([IPython.events]).trigger('event.Namespace');
// To handle it:
// $([IPython.events]).on('event.Namespace',function () {});

@ -13,15 +13,18 @@ var IPython = (function (IPython) {
var utils = IPython.utils;
var Kernel = function () {
// Initialization and connection.
var Kernel = function (base_url) {
this.kernel_id = null;
this.shell_channel = null;
this.iopub_channel = null;
this.base_url = $('body').data('baseKernelUrl') + "kernels";
this.base_url = base_url;
this.running = false;
this.username = "username";
this.session_id = utils.uuid();
this._msg_callbacks = {};
if (typeof(WebSocket) !== 'undefined') {
this.WebSocket = WebSocket;
} else if (typeof(MozWebSocket) !== 'undefined') {
@ -32,7 +35,7 @@ var IPython = (function (IPython) {
};
Kernel.prototype.get_msg = function (msg_type, content) {
Kernel.prototype._get_msg = function (msg_type, content) {
var msg = {
header : {
msg_id : utils.uuid(),
@ -46,46 +49,45 @@ var IPython = (function (IPython) {
return msg;
};
Kernel.prototype.start = function (notebook_id, callback) {
Kernel.prototype.start = function (notebook_id) {
var that = this;
if (!this.running) {
var qs = $.param({notebook:notebook_id});
var url = this.base_url + '?' + qs;
$.post(url,
function (kernel_id) {
that._handle_start_kernel(kernel_id, callback);
},
$.proxy(that._kernel_started,that),
'json'
);
};
};
Kernel.prototype.restart = function (callback) {
Kernel.prototype.restart = function () {
$([IPython.events]).trigger('status_restarting.Kernel');
var url = this.kernel_url + "/restart";
var that = this;
if (this.running) {
this.stop_channels();
var url = this.kernel_url + "/restart";
$.post(url,
function (kernel_id) {
that._handle_start_kernel(kernel_id, callback);
},
$.proxy(that._kernel_started, that),
'json'
);
};
};
Kernel.prototype._handle_start_kernel = function (json, callback) {
Kernel.prototype._kernel_started = function (json) {
console.log("Kernel started: ", json.kernel_id);
this.running = true;
this.kernel_id = json.kernel_id;
this.ws_url = json.ws_url;
this.kernel_url = this.base_url + "/" + this.kernel_id;
this.start_channels();
callback();
this.shell_channel.onmessage = $.proxy(this._handle_shell_reply,this);
this.iopub_channel.onmessage = $.proxy(this._handle_iopub_reply,this);
};
Kernel.prototype._websocket_closed = function(ws_url, early){
var msg;
var parent_item = $('body');
@ -114,7 +116,7 @@ var IPython = (function (IPython) {
}
}
});
};
Kernel.prototype.start_channels = function () {
@ -171,20 +173,58 @@ var IPython = (function (IPython) {
};
};
Kernel.prototype.object_info_request = function (objname) {
// Main public methods.
Kernel.prototype.object_info_request = function (objname, callbacks) {
// When calling this method pass a callbacks structure of the form:
//
// callbacks = {
// 'object_info_reply': object_into_reply_callback
// }
//
// The object_info_reply_callback will be passed the content object of the
// object_into_reply message documented here:
//
// http://ipython.org/ipython-doc/dev/development/messaging.html#object-information
if(typeof(objname)!=null && objname!=null)
{
var content = {
oname : objname.toString(),
};
var msg = this.get_msg("object_info_request", content);
var msg = this._get_msg("object_info_request", content);
this.shell_channel.send(JSON.stringify(msg));
this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
return msg.header.msg_id;
}
return;
}
Kernel.prototype.execute = function (code) {
Kernel.prototype.execute = function (code, callbacks) {
// When calling this method pass a callbacks structure of the form:
//
// callbacks = {
// 'execute_reply': execute_reply_callback,
// 'output': output_callback,
// 'clear_output': clear_output_callback,
// 'cell': cell
// }
//
// The execute_reply_callback will be passed the content object of the execute_reply
// message documented here:
//
// http://ipython.org/ipython-doc/dev/development/messaging.html#execute
//
// The output_callback will be passed msg_type ('stream','display_data','pyout','pyerr')
// of the output and the content object of the PUB/SUB channel that contains the
// output:
//
// http://ipython.org/ipython-doc/dev/development/messaging.html#messages-on-the-pub-sub-socket
//
// The clear_output_callback will be passed a content object that contains
// stdout, stderr and other fields that are booleans.
//
// The cell value will contain the a cell object that the notebook can use for the
// set_next_input payload.
var content = {
code : code,
silent : false,
@ -192,20 +232,32 @@ var IPython = (function (IPython) {
user_expressions : {},
allow_stdin : false
};
var msg = this.get_msg("execute_request", content);
var msg = this._get_msg("execute_request", content);
this.shell_channel.send(JSON.stringify(msg));
this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
return msg.header.msg_id;
};
Kernel.prototype.complete = function (line, cursor_pos) {
Kernel.prototype.complete = function (line, cursor_pos, callbacks) {
// When calling this method pass a callbacks structure of the form:
//
// callbacks = {
// 'complete_reply': complete_reply_callback
// }
//
// The complete_reply_callback will be passed the content object of the
// complete_reply message documented here:
//
// http://ipython.org/ipython-doc/dev/development/messaging.html#complete
var content = {
text : '',
line : line,
cursor_pos : cursor_pos
};
var msg = this.get_msg("complete_request", content);
var msg = this._get_msg("complete_request", content);
this.shell_channel.send(JSON.stringify(msg));
this.set_callbacks_for_msg(msg.header.msg_id, callbacks);
return msg.header.msg_id;
};
@ -229,6 +281,90 @@ var IPython = (function (IPython) {
};
};
// Reply handlers.
Kernel.prototype.get_callbacks_for_msg = function (msg_id) {
var callbacks = this._msg_callbacks[msg_id];
return callbacks;
};
Kernel.prototype.set_callbacks_for_msg = function (msg_id, callbacks) {
this._msg_callbacks[msg_id] = callbacks;
}
Kernel.prototype._handle_shell_reply = function (e) {
reply = $.parseJSON(e.data);
var header = reply.header;
var content = reply.content;
var msg_type = header.msg_type;
var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
if (callbacks !== undefined) {
var cb = callbacks[msg_type];
if (cb !== undefined) {
cb(content);
}
};
if (content.payload !== undefined) {
var payload = content.payload || [];
this._handle_payload(callbacks.cell, payload);
}
};
Kernel.prototype._handle_payload = function (cell, payload) {
var l = payload.length;
// Payloads are handled by triggering events because we don't want the Kernel
// to depend on the Notebook or Pager classes.
for (var i=0; i<l; i++) {
if (payload[i].source === 'IPython.zmq.page.page') {
var data = {'text':payload[i].text}
$([IPython.events]).trigger('open_with_text.Pager', data);
} else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input' && cell !== undefined) {
var data = {'cell': cell, 'text': payload[i].text}
$([IPython.events]).trigger('set_next_input.Notebook', data);
}
};
};
Kernel.prototype._handle_iopub_reply = function (e) {
reply = $.parseJSON(e.data);
var content = reply.content;
var msg_type = reply.header.msg_type;
var callbacks = this.get_callbacks_for_msg(reply.parent_header.msg_id);
if (msg_type !== 'status' && callbacks === undefined) {
// Message not from one of this notebook's cells and there are no
// callbacks to handle it.
return;
}
var output_types = ['stream','display_data','pyout','pyerr'];
if (output_types.indexOf(msg_type) >= 0) {
var cb = callbacks['output'];
if (cb !== undefined) {
cb(msg_type, content);
}
} else if (msg_type === 'status') {
if (content.execution_state === 'busy') {
$([IPython.events]).trigger('status_busy.Kernel');
} else if (content.execution_state === 'idle') {
$([IPython.events]).trigger('status_idle.Kernel');
} else if (content.execution_state === 'dead') {
this.stop_channels();
$([IPython.events]).trigger('status_dead.Kernel');
};
} else if (msg_type === 'clear_output') {
var cb = callbacks['clear_output'];
if (cb !== undefined) {
cb(content);
}
};
};
IPython.Kernel = Kernel;
return IPython;

@ -24,7 +24,6 @@ var IPython = (function (IPython) {
this.clipboard = null;
this.paste_enabled = false;
this.dirty = false;
this.msg_cell_map = {};
this.metadata = {};
this.control_key_active = false;
this.notebook_id = null;
@ -34,9 +33,6 @@ var IPython = (function (IPython) {
this.style();
this.create_elements();
this.bind_events();
this.set_tooltipontab(true);
this.set_smartcompleter(true);
this.set_timebeforetooltip(1200);
};
@ -64,6 +60,20 @@ var IPython = (function (IPython) {
Notebook.prototype.bind_events = function () {
var that = this;
$([IPython.events]).on('set_next_input.Notebook', function (event, data) {
var index = that.find_cell_index(data.cell);
var new_cell = that.insert_cell_below('code',index);
new_cell.set_text(data.text);
that.dirty = true;
});
$([IPython.events]).on('select.Cell', function (event, data) {
var index = that.find_cell_index(data.cell);
that.select(index);
});
$(document).keydown(function (event) {
// console.log(event);
if (that.read_only) return true;
@ -393,19 +403,6 @@ var IPython = (function (IPython) {
};
Notebook.prototype.cell_for_msg = function (msg_id) {
var cell_id = this.msg_cell_map[msg_id];
var result = null;
this.get_cell_elements().filter(function (index) {
cell = $(this).data("cell");
if (cell.cell_id === cell_id) {
result = cell;
};
});
return result;
};
// Cell selection.
Notebook.prototype.select = function (index) {
@ -528,16 +525,16 @@ var IPython = (function (IPython) {
var cell = null;
if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
if (type === 'code') {
cell = new IPython.CodeCell(this);
cell = new IPython.CodeCell(this.kernel);
cell.set_input_prompt();
} else if (type === 'markdown') {
cell = new IPython.MarkdownCell(this);
cell = new IPython.MarkdownCell();
} else if (type === 'html') {
cell = new IPython.HTMLCell(this);
cell = new IPython.HTMLCell();
} else if (type === 'raw') {
cell = new IPython.RawCell(this);
cell = new IPython.RawCell();
} else if (type === 'heading') {
cell = new IPython.HeadingCell(this);
cell = new IPython.HeadingCell();
};
if (cell !== null) {
if (this.ncells() === 0) {
@ -562,16 +559,16 @@ var IPython = (function (IPython) {
var cell = null;
if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
if (type === 'code') {
cell = new IPython.CodeCell(this);
cell = new IPython.CodeCell(this.kernel);
cell.set_input_prompt();
} else if (type === 'markdown') {
cell = new IPython.MarkdownCell(this);
cell = new IPython.MarkdownCell();
} else if (type === 'html') {
cell = new IPython.HTMLCell(this);
cell = new IPython.HTMLCell();
} else if (type === 'raw') {
cell = new IPython.RawCell(this);
cell = new IPython.RawCell();
} else if (type === 'heading') {
cell = new IPython.HeadingCell(this);
cell = new IPython.HeadingCell();
};
if (cell !== null) {
if (this.ncells() === 0) {
@ -864,21 +861,6 @@ var IPython = (function (IPython) {
};
Notebook.prototype.set_timebeforetooltip = function (time) {
this.time_before_tooltip = time;
};
Notebook.prototype.set_tooltipontab = function (state) {
this.tooltip_on_tab = state;
};
Notebook.prototype.set_smartcompleter = function (state) {
this.smart_completer = state;
};
Notebook.prototype.clear_all_output = function () {
var ncells = this.ncells();
var cells = this.get_cells();
@ -903,8 +885,9 @@ var IPython = (function (IPython) {
// Kernel related things
Notebook.prototype.start_kernel = function () {
this.kernel = new IPython.Kernel();
this.kernel.start(this.notebook_id, $.proxy(this.kernel_started, this));
var base_url = $('body').data('baseKernelUrl') + "kernels";
this.kernel = new IPython.Kernel(base_url);
this.kernel.start(this.notebook_id);
};
@ -920,117 +903,7 @@ var IPython = (function (IPython) {
closeText: '',
buttons : {
"Restart": function () {
that.kernel.restart($.proxy(that.kernel_started, that));
$(this).dialog('close');
},
"Continue running": function () {
$(this).dialog('close');
}
}
});
};
Notebook.prototype.kernel_started = function () {
console.log("Kernel started: ", this.kernel.kernel_id);
this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
};
Notebook.prototype.handle_shell_reply = function (e) {
reply = $.parseJSON(e.data);
var header = reply.header;
var content = reply.content;
var msg_type = header.msg_type;
// console.log(reply);
var cell = this.cell_for_msg(reply.parent_header.msg_id);
if (msg_type === "execute_reply") {
cell.set_input_prompt(content.execution_count);
cell.element.removeClass("running");
this.dirty = true;
} else if (msg_type === "complete_reply") {
cell.finish_completing(content.matched_text, content.matches);
} else if (msg_type === "object_info_reply"){
//console.log('back from object_info_request : ')
rep = reply.content;
if(rep.found)
{
cell.finish_tooltip(rep);
}
} else {
//console.log("unknown reply:"+msg_type);
}
// when having a rely from object_info_reply,
// no payload so no nned to handle it
if(typeof(content.payload)!='undefined') {
var payload = content.payload || [];
this.handle_payload(cell, payload);
}
};
Notebook.prototype.handle_payload = function (cell, payload) {
var l = payload.length;
for (var i=0; i<l; i++) {
if (payload[i].source === 'IPython.zmq.page.page') {
if (payload[i].text.trim() !== '') {
IPython.pager.clear();
IPython.pager.expand();
IPython.pager.append_text(payload[i].text);
}
} else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
var index = this.find_cell_index(cell);
var new_cell = this.insert_cell_below('code',index);
new_cell.set_text(payload[i].text);
this.dirty = true;
}
};
};
Notebook.prototype.handle_iopub_reply = function (e) {
reply = $.parseJSON(e.data);
var content = reply.content;
// console.log(reply);
var msg_type = reply.header.msg_type;
var cell = this.cell_for_msg(reply.parent_header.msg_id);
if (msg_type !== 'status' && !cell){
// message not from this notebook, but should be attached to a cell
// console.log("Received IOPub message not caused by one of my cells");
// console.log(reply);
return;
}
var output_types = ['stream','display_data','pyout','pyerr'];
if (output_types.indexOf(msg_type) >= 0) {
this.handle_output(cell, msg_type, content);
} else if (msg_type === 'status') {
if (content.execution_state === 'busy') {
$([IPython.events]).trigger('status_busy.Kernel');
} else if (content.execution_state === 'idle') {
$([IPython.events]).trigger('status_idle.Kernel');
} else if (content.execution_state === 'dead') {
this.handle_status_dead();
};
} else if (msg_type === 'clear_output') {
cell.clear_output(content.stdout, content.stderr, content.other);
};
};
Notebook.prototype.handle_status_dead = function () {
var that = this;
this.kernel.stop_channels();
var dialog = $('<div/>');
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 () {
@ -1041,57 +914,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
@ -1101,12 +923,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();
}
@ -1134,37 +951,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 () {
@ -1290,14 +1076,15 @@ var IPython = (function (IPython) {
Notebook.prototype.load_notebook_success = function (data, status, xhr) {
// Create the kernel before creating cells as they need to be passed it.
if (! this.read_only) {
this.start_kernel();
}
this.fromJSON(data);
if (this.ncells() === 0) {
this.insert_cell_below('code');
};
this.dirty = false;
if (! this.read_only) {
this.start_kernel();
}
this.select(0);
this.scroll_to_top();
if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {

@ -53,6 +53,23 @@ var IPython = (function (IPython) {
$([IPython.events]).on('status_interrupting.Kernel',function () {
that.set_message("Interrupting kernel",500);
});
$([IPython.events]).on('status_dead.Kernel',function () {
IPython.notebook.start_kernel();
that.set_message("Restarting kernel",500);
var dialog = $('<div/>');
dialog.html('The kernel has died and is being restarted.');
$(document).append(dialog);
dialog.dialog({
resizable: false,
modal: true,
title: "Dead kernel",
buttons : {
"OK": function () {
$(this).dialog('close');
}
}
});
});
// Notebook events
$([IPython.events]).on('notebook_loading.Notebook', function () {
that.set_message("Loading notebook",500);

@ -75,6 +75,13 @@ var IPython = (function (IPython) {
that.toggle();
});
$([IPython.events]).on('open_with_text.Pager', function (event, data) {
if (data.text.trim() !== '') {
that.clear();
that.expand();
that.append_text(data.text);
};
});
};

@ -13,7 +13,7 @@ var IPython = (function (IPython) {
// TextCell base class
var TextCell = function (notebook) {
var TextCell = function () {
this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
IPython.Cell.apply(this, arguments);
this.rendered = false;
@ -176,7 +176,7 @@ var IPython = (function (IPython) {
// HTMLCell
var HTMLCell = function (notebook) {
var HTMLCell = function () {
this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
IPython.TextCell.apply(this, arguments);
this.cell_type = 'html';
@ -201,7 +201,7 @@ var IPython = (function (IPython) {
// MarkdownCell
var MarkdownCell = function (notebook) {
var MarkdownCell = function () {
this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
IPython.TextCell.apply(this, arguments);
this.cell_type = 'markdown';
@ -239,7 +239,7 @@ var IPython = (function (IPython) {
// RawCell
var RawCell = function (notebook) {
var RawCell = function () {
this.placeholder = "Type plain text and LaTeX: $\\alpha^2$";
this.code_mirror_mode = 'rst';
IPython.TextCell.apply(this, arguments);
@ -285,7 +285,7 @@ var IPython = (function (IPython) {
// HTMLCell
var HeadingCell = function (notebook) {
var HeadingCell = function () {
this.placeholder = "Type Heading Here";
IPython.TextCell.apply(this, arguments);
this.cell_type = 'heading';

@ -16,8 +16,9 @@ var IPython = (function (IPython) {
var utils = IPython.utils;
// tooltip constructor
var Tooltip = function (notebook) {
var Tooltip = function () {
var that = this;
this.time_before_tooltip = 1200;
// handle to html
this.tooltip = $('#tooltip');
@ -99,10 +100,12 @@ var IPython = (function (IPython) {
// result as invoking `something?`
Tooltip.prototype.showInPager = function()
{
var msg_id = IPython.notebook.kernel.execute(this.name+"?");
IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
this.remove_and_cancel_tooltip();
this._cmfocus();
var that = this;
var callbacks = {'execute_reply': $.proxy(that._handle_execute_reply,that)}
var msg_id = IPython.notebook.kernel.execute(this.name+"?", callbacks);
this.remove_and_cancel_tooltip();
this._cmfocus();
}
// grow the tooltip verticaly
@ -152,7 +155,7 @@ var IPython = (function (IPython) {
Tooltip.prototype.pending = function(cell,text)
{
var that = this;
this.tooltip_timeout = setTimeout(function(){that.request(cell)} , IPython.notebook.time_before_tooltip);
this.tooltip_timeout = setTimeout(function(){that.request(cell)} , that.time_before_tooltip);
}
// make an imediate completion request
@ -203,7 +206,7 @@ var IPython = (function (IPython) {
return;
// don't do anything if line beggin with '(' or is empty
}
IPython.notebook.request_tool_tip(cell, text);
cell.request_tooltip(text);
}
// cancel the option of having the tooltip to stick

Loading…
Cancel
Save