Merge pull request #3605 from ellisonbg/newux

Modal UI - a whole new world of fun....its like vim, but not!
This commit is contained in:
Brian E. Granger 2014-01-10 16:10:25 -08:00
commit d8db72564a
25 changed files with 1886 additions and 553 deletions

View File

@ -65,13 +65,17 @@ IPython.dialog = (function (IPython) {
dialog.remove();
});
}
if (options.reselect_cell !== false) {
dialog.on("hidden", function () {
if (IPython.notebook) {
var cell = IPython.notebook.get_selected_cell();
if (cell) cell.select();
}
});
dialog.on("hidden", function () {
if (IPython.notebook) {
var cell = IPython.notebook.get_selected_cell();
if (cell) cell.select();
IPython.keyboard_manager.enable();
IPython.keyboard_manager.command_mode();
}
});
if (IPython.keyboard_manager) {
IPython.keyboard_manager.disable();
}
return dialog.modal(options);

View File

@ -455,6 +455,26 @@ IPython.utils = (function (IPython) {
return M;
})();
var is_or_has = function (a, b) {
// Is b a child of a or a itself?
return a.has(b).length !==0 || a.is(b);
}
var is_focused = function (e) {
// Is element e, or one of its children focused?
e = $(e);
var target = $(document.activeElement);
if (target.length > 0) {
if (is_or_has(e, target)) {
return true;
} else {
return false;
}
} else {
return false;
}
}
return {
regex_split : regex_split,
@ -475,7 +495,9 @@ IPython.utils = (function (IPython) {
encode_uri_components : encode_uri_components,
splitext : splitext,
always_new : always_new,
browser : browser
browser : browser,
is_or_has : is_or_has,
is_focused : is_focused
};
}(IPython));

View File

@ -39,6 +39,8 @@ var IPython = (function (IPython) {
this.placeholder = options.placeholder || '';
this.read_only = options.cm_config.readOnly;
this.selected = false;
this.rendered = false;
this.mode = 'command';
this.metadata = {};
// load this from metadata later ?
this.user_highlight = 'auto';
@ -60,6 +62,7 @@ var IPython = (function (IPython) {
if (this.element !== null) {
this.element.data("cell", this);
this.bind_events();
this.init_classes();
}
};
@ -97,6 +100,26 @@ var IPython = (function (IPython) {
Cell.prototype.create_element = function () {
};
Cell.prototype.init_classes = function () {
// Call after this.element exists to initialize the css classes
// related to selected, rendered and mode.
if (this.selected) {
this.element.addClass('selected');
} else {
this.element.addClass('unselected');
}
if (this.rendered) {
this.element.addClass('rendered');
} else {
this.element.addClass('unrendered');
}
if (this.mode === 'edit') {
this.element.addClass('edit_mode');
} else {
this.element.addClass('command_mode');
}
}
/**
* Subclasses can implement override bind_events.
@ -108,20 +131,41 @@ var IPython = (function (IPython) {
var that = this;
// We trigger events so that Cell doesn't have to depend on Notebook.
that.element.click(function (event) {
if (that.selected === false) {
if (!that.selected) {
$([IPython.events]).trigger('select.Cell', {'cell':that});
}
};
});
that.element.focusin(function (event) {
if (that.selected === false) {
if (!that.selected) {
$([IPython.events]).trigger('select.Cell', {'cell':that});
}
};
});
if (this.code_mirror) {
this.code_mirror.on("change", function(cm, change) {
$([IPython.events]).trigger("set_dirty.Notebook", {value: true});
});
}
if (this.code_mirror) {
this.code_mirror.on('focus', function(cm, change) {
$([IPython.events]).trigger('edit_mode.Cell', {cell: that});
});
}
if (this.code_mirror) {
this.code_mirror.on('blur', function(cm, change) {
if (that.mode === 'edit') {
setTimeout(function () {
var isf = IPython.utils.is_focused;
var trigger = true;
if (isf('div#tooltip') || isf('div.completions')) {
trigger = false;
}
if (trigger) {
$([IPython.events]).trigger('command_mode.Cell', {cell: that});
}
}, 1);
}
});
}
};
/**
@ -129,29 +173,131 @@ var IPython = (function (IPython) {
* @method typeset
*/
Cell.prototype.typeset = function () {
if (window.MathJax){
if (window.MathJax) {
var cell_math = this.element.get(0);
MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
}
};
/**
* should be triggerd when cell is selected
* handle cell level logic when a cell is selected
* @method select
* @return is the action being taken
*/
Cell.prototype.select = function () {
this.element.addClass('selected');
this.selected = true;
if (!this.selected) {
this.element.addClass('selected');
this.element.removeClass('unselected');
this.selected = true;
return true;
} else {
return false;
}
};
/**
* should be triggerd when cell is unselected
* handle cell level logic when a cell is unselected
* @method unselect
* @return is the action being taken
*/
Cell.prototype.unselect = function () {
this.element.removeClass('selected');
this.selected = false;
if (this.selected) {
this.element.addClass('unselected');
this.element.removeClass('selected');
this.selected = false;
return true;
} else {
return false;
}
};
/**
* handle cell level logic when a cell is rendered
* @method render
* @return is the action being taken
*/
Cell.prototype.render = function () {
if (!this.rendered) {
this.element.addClass('rendered');
this.element.removeClass('unrendered');
this.rendered = true;
return true;
} else {
return false;
}
};
/**
* handle cell level logic when a cell is unrendered
* @method unrender
* @return is the action being taken
*/
Cell.prototype.unrender = function () {
if (this.rendered) {
this.element.addClass('unrendered');
this.element.removeClass('rendered');
this.rendered = false;
return true;
} else {
return false;
}
};
/**
* enter the command mode for the cell
* @method command_mode
* @return is the action being taken
*/
Cell.prototype.command_mode = function () {
if (this.mode !== 'command') {
this.element.addClass('command_mode');
this.element.removeClass('edit_mode');
this.mode = 'command';
return true;
} else {
return false;
}
};
/**
* enter the edit mode for the cell
* @method command_mode
* @return is the action being taken
*/
Cell.prototype.edit_mode = function () {
if (this.mode !== 'edit') {
this.element.addClass('edit_mode');
this.element.removeClass('command_mode');
this.mode = 'edit';
return true;
} else {
return false;
}
}
/**
* Focus the cell in the DOM sense
* @method focus_cell
*/
Cell.prototype.focus_cell = function () {
this.element.focus();
}
/**
* Focus the editor area so a user can type
* @method focus_editor
*/
Cell.prototype.focus_editor = function () {
this.refresh();
this.code_mirror.focus();
}
/**
* Refresh codemirror instance
* @method refresh
*/
Cell.prototype.refresh = function () {
this.code_mirror.refresh();
};
/**
@ -169,30 +315,6 @@ var IPython = (function (IPython) {
Cell.prototype.set_text = function (text) {
};
/**
* Refresh codemirror instance
* @method refresh
*/
Cell.prototype.refresh = function () {
this.code_mirror.refresh();
};
/**
* should be overritten by subclass
* @method edit
**/
Cell.prototype.edit = function () {
};
/**
* should be overritten by subclass
* @method render
**/
Cell.prototype.render = function () {
};
/**
* should be overritten by subclass
* serialise cell to json.

View File

@ -74,7 +74,7 @@ var IPython = (function (IPython) {
var cm_overwrite_options = {
onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
onKeyEvent: $.proxy(this.handle_keyevent,this)
};
options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
@ -139,6 +139,27 @@ var IPython = (function (IPython) {
this.completer = new IPython.Completer(this);
};
/** @method bind_events */
CodeCell.prototype.bind_events = function () {
IPython.Cell.prototype.bind_events.apply(this);
var that = this;
this.element.focusout(
function() { that.auto_highlight(); }
);
};
CodeCell.prototype.handle_keyevent = function (editor, event) {
// console.log('CM', this.mode, event.which, event.type)
if (this.mode === 'command') {
return true;
} else if (this.mode === 'edit') {
return this.handle_codemirror_keyevent(editor, event);
}
};
/**
* This method gets called in CodeMirror's onKeyDown/onKeyPress
* handlers and is used to provide custom key handling. Its return
@ -151,8 +172,9 @@ var IPython = (function (IPython) {
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
var tooltip_closed = null;
if (event.type === 'keydown' && event.which != key.TAB ) {
IPython.tooltip.remove_and_cancel_tooltip();
tooltip_closed = IPython.tooltip.remove_and_cancel_tooltip();
}
var cur = editor.getCursor();
@ -160,7 +182,7 @@ var IPython = (function (IPython) {
this.auto_highlight();
}
if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey || event.altKey)) {
// Always ignore shift-enter in CodeMirror as we handle it.
return true;
} else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
@ -179,8 +201,32 @@ var IPython = (function (IPython) {
} else {
return true;
}
} else if (event.which === key.ESC) {
return IPython.tooltip.remove_and_cancel_tooltip(true);
} else if (event.which === key.ESC && event.type === 'keydown') {
// First see if the tooltip is active and if so cancel it.
if (tooltip_closed) {
// The call to remove_and_cancel_tooltip above in L177 doesn't pass
// force=true. Because of this it won't actually close the tooltip
// if it is in sticky mode. Thus, we have to check again if it is open
// and close it with force=true.
if (!IPython.tooltip._hidden) {
IPython.tooltip.remove_and_cancel_tooltip(true);
}
// If we closed the tooltip, don't let CM or the global handlers
// handle this event.
event.stop();
return true;
}
if (that.code_mirror.options.keyMap === "vim-insert") {
// vim keyMap is active and in insert mode. In this case we leave vim
// insert mode, but remain in notebook edit mode.
// Let' CM handle this event and prevent global handling.
event.stop();
return false;
} else {
// vim keyMap is not active. Leave notebook edit mode.
// Don't let CM handle the event, defer to global handling.
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.
@ -190,7 +236,7 @@ var IPython = (function (IPython) {
} else {
return true;
}
} else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
} else if (event.keyCode === key.TAB && event.type === 'keydown' && event.shiftKey) {
if (editor.somethingSelected()){
var anchor = editor.getCursor("anchor");
var head = editor.getCursor("head");
@ -225,7 +271,6 @@ var IPython = (function (IPython) {
return false;
};
// Kernel related calls.
CodeCell.prototype.set_kernel = function (kernel) {
@ -304,15 +349,32 @@ var IPython = (function (IPython) {
// Basic cell manipulation.
CodeCell.prototype.select = function () {
IPython.Cell.prototype.select.apply(this);
this.code_mirror.refresh();
this.code_mirror.focus();
this.auto_highlight();
// We used to need an additional refresh() after the focus, but
// it appears that this has been fixed in CM. This bug would show
// up on FF when a newly loaded markdown cell was edited.
var cont = IPython.Cell.prototype.select.apply(this);
if (cont) {
this.code_mirror.refresh();
this.auto_highlight();
}
return cont;
};
CodeCell.prototype.render = function () {
var cont = IPython.Cell.prototype.render.apply(this);
// Always execute, even if we are already in the rendered state
return cont;
};
CodeCell.prototype.unrender = function () {
// CodeCell is always rendered
return false;
};
CodeCell.prototype.edit_mode = function () {
var cont = IPython.Cell.prototype.edit_mode.apply(this);
if (cont) {
this.focus_editor();
}
return cont;
}
CodeCell.prototype.select_all = function () {
var start = {line: 0, ch: 0};

View File

@ -218,6 +218,8 @@ var IPython = (function (IPython) {
this.complete = $('<div/>').addClass('completions');
this.complete.attr('id', 'complete');
// Currently webkit doesn't use the size attr correctly. See:
// https://code.google.com/p/chromium/issues/detail?id=4579
this.sel = $('<select style="width: auto"/>')
.attr('multiple', 'true')
.attr('size', Math.min(10, this.raw_result.length));
@ -255,6 +257,7 @@ var IPython = (function (IPython) {
this.build_gui_list(this.raw_result);
this.sel.focus();
IPython.keyboard_manager.disable();
// Opera sometimes ignores focusing a freshly created node
if (window.opera) setTimeout(function () {
if (!this.done) this.sel.focus();
@ -279,6 +282,7 @@ var IPython = (function (IPython) {
if (this.done) return;
this.done = true;
$('.completions').remove();
IPython.keyboard_manager.enable();
}
Completer.prototype.pick = function () {

View File

@ -0,0 +1,683 @@
//----------------------------------------------------------------------------
// Copyright (C) 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.
//----------------------------------------------------------------------------
//============================================================================
// Keyboard management
//============================================================================
var IPython = (function (IPython) {
"use strict";
// Setup global keycodes and inverse keycodes.
// See http://unixpapa.com/js/key.html for a complete description. The short of
// it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
// and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
// but have minor differences.
// These apply to Firefox, (Webkit and IE)
var _keycodes = {
'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
'1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
'7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
'[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
'\\ |': 220, '\' "': 222,
'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
'insert': 45, 'delete': 46, 'numlock': 144,
};
// These apply to Firefox and Opera
var _mozilla_keycodes = {
'; :': 59, '= +': 61, '- _': 173, 'meta': 224
}
// This apply to Webkit and IE
var _ie_keycodes = {
'; :': 186, '= +': 187, '- _': 189,
}
var browser = IPython.utils.browser[0];
if (browser === 'Firefox' || browser === 'Opera') {
$.extend(_keycodes, _mozilla_keycodes);
} else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
$.extend(_keycodes, _ie_keycodes);
}
var keycodes = {};
var inv_keycodes = {};
for (var name in _keycodes) {
var names = name.split(' ');
if (names.length === 1) {
var n = names[0]
keycodes[n] = _keycodes[n]
inv_keycodes[_keycodes[n]] = n
} else {
var primary = names[0];
var secondary = names[1];
keycodes[primary] = _keycodes[name]
keycodes[secondary] = _keycodes[name]
inv_keycodes[_keycodes[name]] = primary
}
}
// Default keyboard shortcuts
var default_common_shortcuts = {
'meta+s' : {
help : 'save notebook',
help_index : 'fb',
handler : function (event) {
IPython.notebook.save_checkpoint();
event.preventDefault();
return false;
}
},
'ctrl+s' : {
help : 'save notebook',
help_index : 'fc',
handler : function (event) {
IPython.notebook.save_checkpoint();
event.preventDefault();
return false;
}
},
'shift' : {
help : '',
help_index : '',
handler : function (event) {
// ignore shift keydown
return true;
}
},
'shift+enter' : {
help : 'run cell',
help_index : 'ba',
handler : function (event) {
IPython.notebook.execute_cell();
return false;
}
},
'ctrl+enter' : {
help : 'run cell, select below',
help_index : 'bb',
handler : function (event) {
IPython.notebook.execute_cell_and_select_below();
return false;
}
},
'alt+enter' : {
help : 'run cell, insert below',
help_index : 'bc',
handler : function (event) {
IPython.notebook.execute_cell_and_insert_below();
return false;
}
}
}
// Edit mode defaults
var default_edit_shortcuts = {
'esc' : {
help : 'command mode',
help_index : 'aa',
handler : function (event) {
IPython.notebook.command_mode();
IPython.notebook.focus_cell();
return false;
}
},
'ctrl+m' : {
help : 'command mode',
help_index : 'ab',
handler : function (event) {
IPython.notebook.command_mode();
IPython.notebook.focus_cell();
return false;
}
},
'up' : {
help : '',
help_index : '',
handler : function (event) {
var cell = IPython.notebook.get_selected_cell();
if (cell && cell.at_top()) {
event.preventDefault();
IPython.notebook.command_mode()
IPython.notebook.select_prev();
IPython.notebook.edit_mode();
return false;
};
}
},
'down' : {
help : '',
help_index : '',
handler : function (event) {
var cell = IPython.notebook.get_selected_cell();
if (cell && cell.at_bottom()) {
event.preventDefault();
IPython.notebook.command_mode()
IPython.notebook.select_next();
IPython.notebook.edit_mode();
return false;
};
}
},
'alt+-' : {
help : 'split cell',
help_index : 'ea',
handler : function (event) {
IPython.notebook.split_cell();
return false;
}
},
'alt+subtract' : {
help : '',
help_index : 'eb',
handler : function (event) {
IPython.notebook.split_cell();
return false;
}
},
}
// Command mode defaults
var default_command_shortcuts = {
'enter' : {
help : 'edit mode',
help_index : 'aa',
handler : function (event) {
IPython.notebook.edit_mode();
return false;
}
},
'up' : {
help : 'select previous cell',
help_index : 'da',
handler : function (event) {
var index = IPython.notebook.get_selected_index();
if (index !== 0 && index !== null) {
IPython.notebook.select_prev();
var cell = IPython.notebook.get_selected_cell();
cell.focus_cell();
};
return false;
}
},
'down' : {
help : 'select next cell',
help_index : 'db',
handler : function (event) {
var index = IPython.notebook.get_selected_index();
if (index !== (IPython.notebook.ncells()-1) && index !== null) {
IPython.notebook.select_next();
var cell = IPython.notebook.get_selected_cell();
cell.focus_cell();
};
return false;
}
},
'k' : {
help : 'select previous cell',
help_index : 'dc',
handler : function (event) {
var index = IPython.notebook.get_selected_index();
if (index !== 0 && index !== null) {
IPython.notebook.select_prev();
var cell = IPython.notebook.get_selected_cell();
cell.focus_cell();
};
return false;
}
},
'j' : {
help : 'select next cell',
help_index : 'dd',
handler : function (event) {
var index = IPython.notebook.get_selected_index();
if (index !== (IPython.notebook.ncells()-1) && index !== null) {
IPython.notebook.select_next();
var cell = IPython.notebook.get_selected_cell();
cell.focus_cell();
};
return false;
}
},
'x' : {
help : 'cut cell',
help_index : 'ee',
handler : function (event) {
IPython.notebook.cut_cell();
return false;
}
},
'c' : {
help : 'copy cell',
help_index : 'ef',
handler : function (event) {
IPython.notebook.copy_cell();
return false;
}
},
'v' : {
help : 'paste cell below',
help_index : 'eg',
handler : function (event) {
IPython.notebook.paste_cell_below();
return false;
}
},
'd' : {
help : 'delete cell (press twice)',
help_index : 'ei',
handler : function (event) {
var dc = IPython.keyboard_manager._delete_count;
if (dc === 0) {
IPython.keyboard_manager._delete_count = 1;
setTimeout(function () {
IPython.keyboard_manager._delete_count = 0;
}, 800);
} else if (dc === 1) {
IPython.notebook.delete_cell();
IPython.keyboard_manager._delete_count = 0;
}
return false;
}
},
'a' : {
help : 'insert cell above',
help_index : 'ec',
handler : function (event) {
IPython.notebook.insert_cell_above('code');
IPython.notebook.select_prev();
IPython.notebook.focus_cell();
return false;
}
},
'b' : {
help : 'insert cell below',
help_index : 'ed',
handler : function (event) {
IPython.notebook.insert_cell_below('code');
IPython.notebook.select_next();
IPython.notebook.focus_cell();
return false;
}
},
'y' : {
help : 'to code',
help_index : 'ca',
handler : function (event) {
IPython.notebook.to_code();
return false;
}
},
'm' : {
help : 'to markdown',
help_index : 'cb',
handler : function (event) {
IPython.notebook.to_markdown();
return false;
}
},
't' : {
help : 'to raw',
help_index : 'cc',
handler : function (event) {
IPython.notebook.to_raw();
return false;
}
},
'1' : {
help : 'to heading 1',
help_index : 'cd',
handler : function (event) {
IPython.notebook.to_heading(undefined, 1);
return false;
}
},
'2' : {
help : 'to heading 2',
help_index : 'ce',
handler : function (event) {
IPython.notebook.to_heading(undefined, 2);
return false;
}
},
'3' : {
help : 'to heading 3',
help_index : 'cf',
handler : function (event) {
IPython.notebook.to_heading(undefined, 3);
return false;
}
},
'4' : {
help : 'to heading 4',
help_index : 'cg',
handler : function (event) {
IPython.notebook.to_heading(undefined, 4);
return false;
}
},
'5' : {
help : 'to heading 5',
help_index : 'ch',
handler : function (event) {
IPython.notebook.to_heading(undefined, 5);
return false;
}
},
'6' : {
help : 'to heading 6',
help_index : 'ci',
handler : function (event) {
IPython.notebook.to_heading(undefined, 6);
return false;
}
},
'o' : {
help : 'toggle output',
help_index : 'gb',
handler : function (event) {
IPython.notebook.toggle_output();
return false;
}
},
'shift+o' : {
help : 'toggle output',
help_index : 'gc',
handler : function (event) {
IPython.notebook.toggle_output_scroll();
return false;
}
},
's' : {
help : 'save notebook',
help_index : 'fa',
handler : function (event) {
IPython.notebook.save_checkpoint();
return false;
}
},
'ctrl+j' : {
help : 'move cell down',
help_index : 'eb',
handler : function (event) {
IPython.notebook.move_cell_down();
return false;
}
},
'ctrl+k' : {
help : 'move cell up',
help_index : 'ea',
handler : function (event) {
IPython.notebook.move_cell_up();
return false;
}
},
'l' : {
help : 'toggle line numbers',
help_index : 'ga',
handler : function (event) {
IPython.notebook.cell_toggle_line_numbers();
return false;
}
},
'i' : {
help : 'interrupt kernel',
help_index : 'ha',
handler : function (event) {
IPython.notebook.kernel.interrupt();
return false;
}
},
'.' : {
help : 'restart kernel',
help_index : 'hb',
handler : function (event) {
IPython.notebook.restart_kernel();
return false;
}
},
'h' : {
help : 'keyboard shortcuts',
help_index : 'gd',
handler : function (event) {
IPython.quick_help.show_keyboard_shortcuts();
return false;
}
},
'z' : {
help : 'undo last delete',
help_index : 'eh',
handler : function (event) {
IPython.notebook.undelete_cell();
return false;
}
},
'shift+=' : {
help : 'merge cell below',
help_index : 'ej',
handler : function (event) {
IPython.notebook.merge_cell_below();
return false;
}
},
}
// Shortcut manager class
var ShortcutManager = function () {
this._shortcuts = {}
}
ShortcutManager.prototype.help = function () {
var help = [];
for (var shortcut in this._shortcuts) {
var help_string = this._shortcuts[shortcut]['help'];
var help_index = this._shortcuts[shortcut]['help_index'];
if (help_string) {
help.push({
shortcut: shortcut,
help: help_string,
help_index: help_index}
);
}
}
help.sort(function (a, b) {
if (a.help_index > b.help_index)
return 1;
if (a.help_index < b.help_index)
return -1;
return 0;
});
return help;
}
ShortcutManager.prototype.normalize_key = function (key) {
return inv_keycodes[keycodes[key]];
}
ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
// Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
var values = shortcut.split("+");
if (values.length === 1) {
return this.normalize_key(values[0])
} else {
var modifiers = values.slice(0,-1);
var key = this.normalize_key(values[values.length-1]);
modifiers.sort();
return modifiers.join('+') + '+' + key;
}
}
ShortcutManager.prototype.event_to_shortcut = function (event) {
// Convert a jQuery keyboard event to a strong based keyboard shortcut
var shortcut = '';
var key = inv_keycodes[event.which]
if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
shortcut += key;
return shortcut
}
ShortcutManager.prototype.clear_shortcuts = function () {
this._shortcuts = {};
}
ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
if (typeof(data) === 'function') {
data = {help: '', help_index: '', handler: data}
}
data.help_index = data.help_index || '';
data.help = data.help || '';
if (data.help_index === '') {
data.help_index = 'zz';
}
shortcut = this.normalize_shortcut(shortcut);
this._shortcuts[shortcut] = data;
}
ShortcutManager.prototype.add_shortcuts = function (data) {
for (var shortcut in data) {
this.add_shortcut(shortcut, data[shortcut]);
}
}
ShortcutManager.prototype.remove_shortcut = function (shortcut) {
shortcut = this.normalize_shortcut(shortcut);
delete this._shortcuts[shortcut];
}
ShortcutManager.prototype.call_handler = function (event) {
var shortcut = this.event_to_shortcut(event);
var data = this._shortcuts[shortcut];
if (data !== undefined) {
var handler = data['handler'];
if (handler !== undefined) {
return handler(event);
}
}
return true;
}
// Main keyboard manager for the notebook
var KeyboardManager = function () {
this.mode = 'command';
this.enabled = true;
this._delete_count = 0;
this.bind_events();
this.command_shortcuts = new ShortcutManager();
this.command_shortcuts.add_shortcuts(default_common_shortcuts);
this.command_shortcuts.add_shortcuts(default_command_shortcuts);
this.edit_shortcuts = new ShortcutManager();
this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
};
KeyboardManager.prototype.bind_events = function () {
var that = this;
$(document).keydown(function (event) {
return that.handle_keydown(event);
});
};
KeyboardManager.prototype.handle_keydown = function (event) {
var notebook = IPython.notebook;
if (event.which === keycodes['esc']) {
// Intercept escape at highest level to avoid closing
// websocket connection with firefox
event.preventDefault();
}
if (!this.enabled) {
if (event.which === keycodes['esc']) {
// ESC
notebook.command_mode();
return false;
}
return true;
}
if (this.mode === 'edit') {
return this.edit_shortcuts.call_handler(event);
} else if (this.mode === 'command') {
return this.command_shortcuts.call_handler(event);
}
return true;
}
KeyboardManager.prototype.edit_mode = function () {
this.last_mode = this.mode;
this.mode = 'edit';
}
KeyboardManager.prototype.command_mode = function () {
this.last_mode = this.mode;
this.mode = 'command';
}
KeyboardManager.prototype.enable = function () {
this.enabled = true;
}
KeyboardManager.prototype.disable = function () {
this.enabled = false;
}
KeyboardManager.prototype.register_events = function (e) {
var that = this;
e.on('focusin', function () {
that.command_mode();
that.disable();
});
e.on('focusout', function () {
that.command_mode();
that.enable();
});
// There are times (raw_input) where we remove the element from the DOM before
// focusout is called. In this case we bind to the remove event of jQueryUI,
// which gets triggered upon removal.
e.on('remove', function () {
that.command_mode();
that.enable();
});
}
IPython.keycodes = keycodes;
IPython.inv_keycodes = inv_keycodes;
IPython.default_common_shortcuts = default_common_shortcuts;
IPython.default_edit_shortcuts = default_edit_shortcuts;
IPython.default_command_shortcuts = default_command_shortcuts;
IPython.ShortcutManager = ShortcutManager;
IPython.KeyboardManager = KeyboardManager;
return IPython;
}(IPython));

View File

@ -62,6 +62,7 @@ function (marked) {
IPython.quick_help = new IPython.QuickHelp();
IPython.login_widget = new IPython.LoginWidget('span#login_widget',{baseProjectUrl:baseProjectUrl});
IPython.notebook = new IPython.Notebook('div#notebook',{baseProjectUrl:baseProjectUrl, notebookPath:notebookPath, notebookName:notebookName});
IPython.keyboard_manager = new IPython.KeyboardManager();
IPython.save_widget = new IPython.SaveWidget('span#save_widget');
IPython.menubar = new IPython.MenuBar('#menubar',{baseProjectUrl:baseProjectUrl, notebookPath: notebookPath})
IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container')

View File

@ -41,6 +41,8 @@ var IPython = (function (IPython) {
icon : 'icon-plus-sign',
callback : function () {
IPython.notebook.insert_cell_below('code');
IPython.notebook.select_next();
IPython.notebook.focus_cell();
}
}
],'insert_above_below');
@ -98,7 +100,7 @@ var IPython = (function (IPython) {
label : 'Run Cell',
icon : 'icon-play',
callback : function () {
IPython.notebook.execute_selected_cell();
IPython.notebook.execute_cell();
}
},
{

View File

@ -161,7 +161,7 @@ var IPython = (function (IPython) {
IPython.notebook.delete_cell();
});
this.element.find('#undelete_cell').click(function () {
IPython.notebook.undelete();
IPython.notebook.undelete_cell();
});
this.element.find('#split_cell').click(function () {
IPython.notebook.split_cell();
@ -200,16 +200,21 @@ var IPython = (function (IPython) {
// Insert
this.element.find('#insert_cell_above').click(function () {
IPython.notebook.insert_cell_above('code');
IPython.notebook.select_prev();
});
this.element.find('#insert_cell_below').click(function () {
IPython.notebook.insert_cell_below('code');
IPython.notebook.select_next();
});
// Cell
this.element.find('#run_cell').click(function () {
IPython.notebook.execute_selected_cell();
IPython.notebook.execute_cell();
});
this.element.find('#run_cell_in_place').click(function () {
IPython.notebook.execute_selected_cell({terminal:true});
this.element.find('#run_cell_select_below').click(function () {
IPython.notebook.execute_cell_and_select_below();
});
this.element.find('#run_cell_insert_below').click(function () {
IPython.notebook.execute_cell_and_insert_below();
});
this.element.find('#run_all_cells').click(function () {
IPython.notebook.execute_all_cells();

View File

@ -13,7 +13,6 @@ var IPython = (function (IPython) {
"use strict";
var utils = IPython.utils;
var key = IPython.utils.keycodes;
/**
* A notebook contains and manages cells.
@ -39,6 +38,9 @@ var IPython = (function (IPython) {
this.undelete_index = null;
this.undelete_below = false;
this.paste_enabled = false;
// It is important to start out in command mode to match the intial mode
// of the KeyboardManager.
this.mode = 'command';
this.set_dirty(false);
this.metadata = {};
this._checkpoint_after_save = false;
@ -50,7 +52,6 @@ var IPython = (function (IPython) {
this.minimum_autosave_interval = 120000;
// single worksheet for now
this.worksheet_metadata = {};
this.control_key_active = false;
this.notebook_name_blacklist_re = /[\/\\:]/;
this.nbformat = 3 // Increment this when changing the nbformat
this.nbformat_minor = 0 // Increment this when changing the nbformat
@ -74,7 +75,7 @@ var IPython = (function (IPython) {
* @method baseProjectUrl
* @return {String} The base project URL
*/
Notebook.prototype.baseProjectUrl = function(){
Notebook.prototype.baseProjectUrl = function() {
return this._baseProjectUrl || $('body').data('baseProjectUrl');
};
@ -92,12 +93,13 @@ var IPython = (function (IPython) {
* @method create_elements
*/
Notebook.prototype.create_elements = function () {
var that = this;
this.element.attr('tabindex','-1');
this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
// We add this end_space div to the end of the notebook div to:
// i) provide a margin between the last cell and the end of the notebook
// ii) to prevent the div from scrolling up when the last cell is being
// edited, but is too low on the page, which browsers will do automatically.
var that = this;
this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
var end_space = $('<div/>').addClass('end_space');
end_space.dblclick(function (e) {
var ncells = that.ncells();
@ -105,7 +107,6 @@ var IPython = (function (IPython) {
});
this.element.append(this.container);
this.container.append(end_space);
$('div#notebook').addClass('border-box-sizing');
};
/**
@ -131,7 +132,17 @@ var IPython = (function (IPython) {
var index = that.find_cell_index(data.cell);
that.select(index);
});
$([IPython.events]).on('edit_mode.Cell', function (event, data) {
var index = that.find_cell_index(data.cell);
that.select(index);
that.edit_mode();
});
$([IPython.events]).on('command_mode.Cell', function (event, data) {
that.command_mode();
});
$([IPython.events]).on('status_autorestarting.Kernel', function () {
IPython.dialog.modal({
title: "Kernel Restarting",
@ -144,220 +155,25 @@ var IPython = (function (IPython) {
});
});
$(document).keydown(function (event) {
// Save (CTRL+S) or (AppleKey+S)
//metaKey = applekey on mac
if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
that.save_checkpoint();
event.preventDefault();
return false;
} else if (event.which === key.ESC) {
// Intercept escape at highest level to avoid closing
// websocket connection with firefox
IPython.pager.collapse();
event.preventDefault();
} else if (event.which === key.SHIFT) {
// ignore shift keydown
return true;
}
if (event.which === key.UPARROW && !event.shiftKey) {
var cell = that.get_selected_cell();
if (cell && cell.at_top()) {
event.preventDefault();
that.select_prev();
};
} else if (event.which === key.DOWNARROW && !event.shiftKey) {
var cell = that.get_selected_cell();
if (cell && cell.at_bottom()) {
event.preventDefault();
that.select_next();
};
} else if (event.which === key.ENTER && event.shiftKey) {
that.execute_selected_cell();
return false;
} else if (event.which === key.ENTER && event.altKey) {
// Execute code cell, and insert new in place
that.execute_selected_cell();
// Only insert a new cell, if we ended up in an already populated cell
if (/\S/.test(that.get_selected_cell().get_text()) == true) {
that.insert_cell_above('code');
}
return false;
} else if (event.which === key.ENTER && event.ctrlKey) {
that.execute_selected_cell({terminal:true});
return false;
} else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
that.control_key_active = true;
return false;
} else if (event.which === 88 && that.control_key_active) {
// Cut selected cell = x
that.cut_cell();
that.control_key_active = false;
return false;
} else if (event.which === 67 && that.control_key_active) {
// Copy selected cell = c
that.copy_cell();
that.control_key_active = false;
return false;
} else if (event.which === 86 && that.control_key_active) {
// Paste below selected cell = v
that.paste_cell_below();
that.control_key_active = false;
return false;
} else if (event.which === 68 && that.control_key_active) {
// Delete selected cell = d
that.delete_cell();
that.control_key_active = false;
return false;
} else if (event.which === 65 && that.control_key_active) {
// Insert code cell above selected = a
that.insert_cell_above('code');
that.control_key_active = false;
return false;
} else if (event.which === 66 && that.control_key_active) {
// Insert code cell below selected = b
that.insert_cell_below('code');
that.control_key_active = false;
return false;
} else if (event.which === 89 && that.control_key_active) {
// To code = y
that.to_code();
that.control_key_active = false;
return false;
} else if (event.which === 77 && that.control_key_active) {
// To markdown = m
that.to_markdown();
that.control_key_active = false;
return false;
} else if (event.which === 84 && that.control_key_active) {
// To Raw = t
that.to_raw();
that.control_key_active = false;
return false;
} else if (event.which === 49 && that.control_key_active) {
// To Heading 1 = 1
that.to_heading(undefined, 1);
that.control_key_active = false;
return false;
} else if (event.which === 50 && that.control_key_active) {
// To Heading 2 = 2
that.to_heading(undefined, 2);
that.control_key_active = false;
return false;
} else if (event.which === 51 && that.control_key_active) {
// To Heading 3 = 3
that.to_heading(undefined, 3);
that.control_key_active = false;
return false;
} else if (event.which === 52 && that.control_key_active) {
// To Heading 4 = 4
that.to_heading(undefined, 4);
that.control_key_active = false;
return false;
} else if (event.which === 53 && that.control_key_active) {
// To Heading 5 = 5
that.to_heading(undefined, 5);
that.control_key_active = false;
return false;
} else if (event.which === 54 && that.control_key_active) {
// To Heading 6 = 6
that.to_heading(undefined, 6);
that.control_key_active = false;
return false;
} else if (event.which === 79 && that.control_key_active) {
// Toggle output = o
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) {
// Save notebook = s
that.save_checkpoint();
that.control_key_active = false;
return false;
} else if (event.which === 74 && that.control_key_active) {
// Move cell down = j
that.move_cell_down();
that.control_key_active = false;
return false;
} else if (event.which === 75 && that.control_key_active) {
// Move cell up = k
that.move_cell_up();
that.control_key_active = false;
return false;
} else if (event.which === 80 && that.control_key_active) {
// Select previous = p
that.select_prev();
that.control_key_active = false;
return false;
} else if (event.which === 78 && that.control_key_active) {
// Select next = n
that.select_next();
that.control_key_active = false;
return false;
} else if (event.which === 76 && that.control_key_active) {
// Toggle line numbers = l
that.cell_toggle_line_numbers();
that.control_key_active = false;
return false;
} else if (event.which === 73 && that.control_key_active) {
// Interrupt kernel = i
that.session.interrupt_kernel();
that.control_key_active = false;
return false;
} else if (event.which === 190 && that.control_key_active) {
// Restart kernel = . # matches qt console
that.restart_kernel();
that.control_key_active = false;
return false;
} else if (event.which === 72 && that.control_key_active) {
// Show keyboard shortcuts = h
IPython.quick_help.show_keyboard_shortcuts();
that.control_key_active = false;
return false;
} else if (event.which === 90 && that.control_key_active) {
// Undo last cell delete = z
that.undelete();
that.control_key_active = false;
return false;
} else if ((event.which === 189 || event.which === 173) &&
that.control_key_active) {
// how fun! '-' is 189 in Chrome, but 173 in FF and Opera
// Split cell = -
that.split_cell();
that.control_key_active = false;
return false;
} else if (that.control_key_active) {
that.control_key_active = false;
return true;
}
return true;
});
var collapse_time = function(time){
var collapse_time = function (time) {
var app_height = $('#ipython-main-app').height(); // content height
var splitter_height = $('div#pager_splitter').outerHeight(true);
var new_height = app_height - splitter_height;
that.element.animate({height : new_height + 'px'}, time);
}
};
this.element.bind('collapse_pager', function (event,extrap) {
this.element.bind('collapse_pager', function (event, extrap) {
var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
collapse_time(time);
});
var expand_time = function(time) {
var expand_time = function (time) {
var app_height = $('#ipython-main-app').height(); // content height
var splitter_height = $('div#pager_splitter').outerHeight(true);
var pager_height = $('div#pager').outerHeight(true);
var new_height = app_height - pager_height - splitter_height;
that.element.animate({height : new_height + 'px'}, time);
}
};
this.element.bind('expand_pager', function (event, extrap) {
var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
@ -650,6 +466,7 @@ var IPython = (function (IPython) {
if (this.is_valid_cell_index(index)) {
var sindex = this.get_selected_index()
if (sindex !== null && index !== sindex) {
this.command_mode();
this.get_cell(sindex).unselect();
};
var cell = this.get_cell(index);
@ -692,6 +509,48 @@ var IPython = (function (IPython) {
};
// Edit/Command mode
Notebook.prototype.get_edit_index = function () {
var result = null;
this.get_cell_elements().filter(function (index) {
if ($(this).data("cell").mode === 'edit') {
result = index;
};
});
return result;
};
Notebook.prototype.command_mode = function () {
if (this.mode !== 'command') {
var index = this.get_edit_index();
var cell = this.get_cell(index);
if (cell) {
cell.command_mode();
};
this.mode = 'command';
IPython.keyboard_manager.command_mode();
};
};
Notebook.prototype.edit_mode = function () {
if (this.mode !== 'edit') {
var cell = this.get_selected_cell();
if (cell === null) {return;} // No cell is selected
// We need to set the mode to edit to prevent reentering this method
// when cell.edit_mode() is called below.
this.mode = 'edit';
IPython.keyboard_manager.edit_mode();
cell.edit_mode();
};
};
Notebook.prototype.focus_cell = function () {
var cell = this.get_selected_cell();
if (cell === null) {return;} // No cell is selected
cell.focus_cell();
};
// Cell movement
/**
@ -710,6 +569,8 @@ var IPython = (function (IPython) {
tomove.detach();
pivot.before(tomove);
this.select(i-1);
var cell = this.get_selected_cell();
cell.focus_cell();
};
this.set_dirty(true);
};
@ -726,13 +587,15 @@ var IPython = (function (IPython) {
**/
Notebook.prototype.move_cell_down = function (index) {
var i = this.index_or_selected(index);
if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
var pivot = this.get_cell_element(i+1);
var tomove = this.get_cell_element(i);
if (pivot !== null && tomove !== null) {
tomove.detach();
pivot.after(tomove);
this.select(i+1);
var cell = this.get_selected_cell();
cell.focus_cell();
};
};
this.set_dirty();
@ -755,9 +618,18 @@ var IPython = (function (IPython) {
this.undelete_backup = cell.toJSON();
$('#undelete_cell').removeClass('disabled');
if (this.is_valid_cell_index(i)) {
var old_ncells = this.ncells();
var ce = this.get_cell_element(i);
ce.remove();
if (i === (this.ncells())) {
if (i === 0) {
// Always make sure we have at least one cell.
if (old_ncells === 1) {
this.insert_cell_below('code');
}
this.select(0);
this.undelete_index = 0;
this.undelete_below = false;
} else if (i === old_ncells-1 && i !== 0) {
this.select(i-1);
this.undelete_index = i - 1;
this.undelete_below = true;
@ -772,6 +644,42 @@ var IPython = (function (IPython) {
return this;
};
/**
* Restore the most recently deleted cell.
*
* @method undelete
*/
Notebook.prototype.undelete_cell = function() {
if (this.undelete_backup !== null && this.undelete_index !== null) {
var current_index = this.get_selected_index();
if (this.undelete_index < current_index) {
current_index = current_index + 1;
}
if (this.undelete_index >= this.ncells()) {
this.select(this.ncells() - 1);
}
else {
this.select(this.undelete_index);
}
var cell_data = this.undelete_backup;
var new_cell = null;
if (this.undelete_below) {
new_cell = this.insert_cell_below(cell_data.cell_type);
} else {
new_cell = this.insert_cell_above(cell_data.cell_type);
}
new_cell.fromJSON(cell_data);
if (this.undelete_below) {
this.select(current_index+1);
} else {
this.select(current_index);
}
this.undelete_backup = null;
this.undelete_index = null;
}
$('#undelete_cell').addClass('disabled');
}
/**
* Insert a cell so that after insertion the cell is at given index.
*
@ -804,10 +712,14 @@ var IPython = (function (IPython) {
cell = new IPython.HeadingCell();
}
if(this._insert_element_at_index(cell.element,index)){
if(this._insert_element_at_index(cell.element,index)) {
cell.render();
this.select(this.find_cell_index(cell));
$([IPython.events]).trigger('create.Cell', {'cell': cell, 'index': index});
cell.refresh();
// We used to select the cell after we refresh it, but there
// are now cases were this method is called where select is
// not appropriate. The selection logic should be handled by the
// caller of the the top level insert_cell methods.
this.set_dirty(true);
}
}
@ -923,6 +835,8 @@ var IPython = (function (IPython) {
// to this state, instead of a blank cell
target_cell.code_mirror.clearHistory();
source_element.remove();
this.select(i);
this.edit_mode();
this.set_dirty(true);
};
};
@ -945,13 +859,15 @@ var IPython = (function (IPython) {
if (text === source_cell.placeholder) {
text = '';
};
// The edit must come before the set_text.
target_cell.edit();
// We must show the editor before setting its contents
target_cell.unrender();
target_cell.set_text(text);
// make this value the starting point, so that we can only undo
// to this state, instead of a blank cell
target_cell.code_mirror.clearHistory();
source_element.remove();
this.select(i);
this.edit_mode();
this.set_dirty(true);
};
};
@ -975,13 +891,15 @@ var IPython = (function (IPython) {
if (text === source_cell.placeholder) {
text = '';
};
// The edit must come before the set_text.
target_cell.edit();
// We must show the editor before setting its contents
target_cell.unrender();
target_cell.set_text(text);
// make this value the starting point, so that we can only undo
// to this state, instead of a blank cell
target_cell.code_mirror.clearHistory();
source_element.remove();
this.select(i);
this.edit_mode();
this.set_dirty(true);
};
};
@ -1009,16 +927,18 @@ var IPython = (function (IPython) {
if (text === source_cell.placeholder) {
text = '';
};
// The edit must come before the set_text.
// We must show the editor before setting its contents
target_cell.set_level(level);
target_cell.edit();
target_cell.unrender();
target_cell.set_text(text);
// make this value the starting point, so that we can only undo
// to this state, instead of a blank cell
target_cell.code_mirror.clearHistory();
source_element.remove();
this.set_dirty(true);
this.select(i);
};
this.edit_mode();
this.set_dirty(true);
$([IPython.events]).trigger('selected_cell_type_changed.Notebook',
{'cell_type':'heading',level:level}
);
@ -1123,40 +1043,6 @@ var IPython = (function (IPython) {
};
};
// Cell undelete
/**
* Restore the most recently deleted cell.
*
* @method undelete
*/
Notebook.prototype.undelete = function() {
if (this.undelete_backup !== null && this.undelete_index !== null) {
var current_index = this.get_selected_index();
if (this.undelete_index < current_index) {
current_index = current_index + 1;
}
if (this.undelete_index >= this.ncells()) {
this.select(this.ncells() - 1);
}
else {
this.select(this.undelete_index);
}
var cell_data = this.undelete_backup;
var new_cell = null;
if (this.undelete_below) {
new_cell = this.insert_cell_below(cell_data.cell_type);
} else {
new_cell = this.insert_cell_above(cell_data.cell_type);
}
new_cell.fromJSON(cell_data);
this.select(current_index);
this.undelete_backup = null;
this.undelete_index = null;
}
$('#undelete_cell').addClass('disabled');
}
// Split/merge
/**
@ -1165,24 +1051,25 @@ var IPython = (function (IPython) {
* @method split_cell
*/
Notebook.prototype.split_cell = function () {
// Todo: implement spliting for other cell types.
var mdc = IPython.MarkdownCell;
var rc = IPython.RawCell;
var cell = this.get_selected_cell();
if (cell.is_splittable()) {
var texta = cell.get_pre_cursor();
var textb = cell.get_post_cursor();
if (cell instanceof IPython.CodeCell) {
// In this case the operations keep the notebook in its existing mode
// so we don't need to do any post-op mode changes.
cell.set_text(textb);
var new_cell = this.insert_cell_above('code');
new_cell.set_text(texta);
this.select_next();
} else if (cell instanceof IPython.MarkdownCell) {
} else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
// We know cell is !rendered so we can use set_text.
cell.set_text(textb);
cell.render();
var new_cell = this.insert_cell_above('markdown');
new_cell.edit(); // editor must be visible to call set_text
var new_cell = this.insert_cell_above(cell.cell_type);
// Unrender the new cell so we can call set_text.
new_cell.unrender();
new_cell.set_text(texta);
new_cell.render();
this.select_next();
}
};
};
@ -1193,8 +1080,11 @@ var IPython = (function (IPython) {
* @method merge_cell_above
*/
Notebook.prototype.merge_cell_above = function () {
var mdc = IPython.MarkdownCell;
var rc = IPython.RawCell;
var index = this.get_selected_index();
var cell = this.get_cell(index);
var render = cell.rendered;
if (!cell.is_mergeable()) {
return;
}
@ -1207,10 +1097,14 @@ var IPython = (function (IPython) {
var text = cell.get_text();
if (cell instanceof IPython.CodeCell) {
cell.set_text(upper_text+'\n'+text);
} else if (cell instanceof IPython.MarkdownCell) {
cell.edit();
cell.set_text(upper_text+'\n'+text);
cell.render();
} else if ((cell instanceof mdc) || (cell instanceof rc)) {
cell.unrender(); // Must unrender before we set_text.
cell.set_text(upper_text+'\n\n'+text);
if (render) {
// The rendered state of the final cell should match
// that of the original selected cell;
cell.render();
}
};
this.delete_cell(index-1);
this.select(this.find_cell_index(cell));
@ -1223,8 +1117,11 @@ var IPython = (function (IPython) {
* @method merge_cell_below
*/
Notebook.prototype.merge_cell_below = function () {
var mdc = IPython.MarkdownCell;
var rc = IPython.RawCell;
var index = this.get_selected_index();
var cell = this.get_cell(index);
var render = cell.rendered;
if (!cell.is_mergeable()) {
return;
}
@ -1237,10 +1134,14 @@ var IPython = (function (IPython) {
var text = cell.get_text();
if (cell instanceof IPython.CodeCell) {
cell.set_text(text+'\n'+lower_text);
} else if (cell instanceof IPython.MarkdownCell) {
cell.edit();
cell.set_text(text+'\n'+lower_text);
cell.render();
} else if ((cell instanceof mdc) || (cell instanceof rc)) {
cell.unrender(); // Must unrender before we set_text.
cell.set_text(text+'\n\n'+lower_text);
if (render) {
// The rendered state of the final cell should match
// that of the original selected cell;
cell.render();
}
};
this.delete_cell(index+1);
this.select(this.find_cell_index(cell));
@ -1433,35 +1334,76 @@ var IPython = (function (IPython) {
};
/**
* Run the selected cell.
* Execute or render cell outputs and go into command mode.
*
* Execute or render cell outputs.
*
* @method execute_selected_cell
* @param {Object} options Customize post-execution behavior
* @method execute_cell
*/
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
var default_options = {terminal: false, add_new: true};
$.extend(default_options, options);
var that = this;
var cell = that.get_selected_cell();
var cell_index = that.find_cell_index(cell);
if (cell instanceof IPython.CodeCell) {
cell.execute();
Notebook.prototype.execute_cell = function () {
// mode = shift, ctrl, alt
var cell = this.get_selected_cell();
var cell_index = this.find_cell_index(cell);
cell.execute();
this.command_mode();
cell.focus_cell();
this.set_dirty(true);
}
/**
* Execute or render cell outputs and insert a new cell below.
*
* @method execute_cell_and_insert_below
*/
Notebook.prototype.execute_cell_and_insert_below = function () {
var cell = this.get_selected_cell();
var cell_index = this.find_cell_index(cell);
cell.execute();
// If we are at the end always insert a new cell and return
if (cell_index === (this.ncells()-1)) {
this.insert_cell_below('code');
this.select(cell_index+1);
this.edit_mode();
this.scroll_to_bottom();
this.set_dirty(true);
return;
}
if (default_options.terminal) {
cell.select_all();
} else {
if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
that.insert_cell_below('code');
// If we are adding a new cell at the end, scroll down to show it.
that.scroll_to_bottom();
} else {
that.select(cell_index+1);
};
};
// Only insert a new cell, if we ended up in an already populated cell
var next_text = this.get_cell(cell_index+1).get_text();
if (/\S/.test(next_text) === true) {
this.insert_cell_below('code');
}
this.select(cell_index+1);
this.edit_mode();
this.set_dirty(true);
};
/**
* Execute or render cell outputs and select the next cell.
*
* @method execute_cell_and_select_below
*/
Notebook.prototype.execute_cell_and_select_below = function () {
var cell = this.get_selected_cell();
var cell_index = this.find_cell_index(cell);
cell.execute();
// If we are at the end always insert a new cell and return
if (cell_index === (this.ncells()-1)) {
this.insert_cell_below('code');
this.select(cell_index+1);
this.edit_mode();
this.scroll_to_bottom();
this.set_dirty(true);
return;
}
this.select(cell_index+1);
this.get_cell(cell_index+1).focus_cell();
this.set_dirty(true);
};
@ -1504,7 +1446,7 @@ var IPython = (function (IPython) {
Notebook.prototype.execute_cell_range = function (start, end) {
for (var i=start; i<end; i++) {
this.select(i);
this.execute_selected_cell({add_new:false});
this.execute_cell();
};
};
@ -1584,7 +1526,7 @@ var IPython = (function (IPython) {
cell_data.cell_type = 'raw';
}
new_cell = this.insert_cell_at_bottom(cell_data.cell_type);
new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
new_cell.fromJSON(cell_data);
};
};
@ -1909,9 +1851,13 @@ var IPython = (function (IPython) {
this.fromJSON(data);
if (this.ncells() === 0) {
this.insert_cell_below('code');
this.select(0);
this.edit_mode();
} else {
this.select(0);
this.command_mode();
};
this.set_dirty(false);
this.select(0);
this.scroll_to_top();
if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
var msg = "This notebook has been converted from an older " +

View File

@ -509,6 +509,7 @@ var IPython = (function (IPython) {
OutputArea.prototype.append_html = function (html, md, element) {
var toinsert = this.create_output_subarea(md, "output_html rendered_html");
IPython.keyboard_manager.register_events(toinsert);
toinsert.append(html);
element.append(toinsert);
};
@ -517,6 +518,7 @@ var IPython = (function (IPython) {
OutputArea.prototype.append_javascript = function (js, md, container) {
// We just eval the JS code, element appears in the local scope.
var element = this.create_output_subarea(md, "output_javascript");
IPython.keyboard_manager.register_events(element);
container.append(element);
try {
eval(js);
@ -646,11 +648,18 @@ var IPython = (function (IPython) {
})
)
);
this.element.append(area);
// weirdly need double-focus now,
// otherwise only the cell will be focused
area.find("input.raw_input").focus().focus();
var raw_input = area.find('input.raw_input');
// Register events that enable/disable the keyboard manager while raw
// input is focused.
IPython.keyboard_manager.register_events(raw_input);
// Note, the following line used to read raw_input.focus().focus().
// This seemed to be needed otherwise only the cell would be focused.
// But with the modal UI, this seems to work fine with one call to focus().
raw_input.focus();
}
OutputArea.prototype._submit_raw_input = function (evt) {
var container = this.element.find("div.raw_input");
var theprompt = container.find("span.input_prompt");

View File

@ -23,44 +23,35 @@ var IPython = (function (IPython) {
$(this.shortcut_dialog).modal("toggle");
return;
}
var body = $('<div/>');
var shortcuts = [
{key: 'Shift-Enter', help: 'run cell'},
{key: 'Ctrl-Enter', help: 'run cell in-place'},
{key: 'Alt-Enter', help: 'run cell, insert below'},
{key: 'Ctrl-m x', help: 'cut cell'},
{key: 'Ctrl-m c', help: 'copy cell'},
{key: 'Ctrl-m v', help: 'paste cell'},
{key: 'Ctrl-m d', help: 'delete cell'},
{key: 'Ctrl-m z', help: 'undo last cell deletion'},
{key: 'Ctrl-m -', help: 'split cell'},
{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'},
{key: 'Ctrl-m k', help: 'move cell up'},
{key: 'Ctrl-m y', help: 'code cell'},
{key: 'Ctrl-m m', help: 'markdown cell'},
{key: 'Ctrl-m t', help: 'raw cell'},
{key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
{key: 'Ctrl-m p', help: 'select previous'},
{key: 'Ctrl-m n', help: 'select next'},
{key: 'Ctrl-m i', help: 'interrupt kernel'},
{key: 'Ctrl-m .', help: 'restart kernel'},
{key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
];
for (var i=0; i<shortcuts.length; i++) {
body.append($('<div>').addClass('quickhelp').
append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
);
};
var command_shortcuts = IPython.keyboard_manager.command_shortcuts.help();
var edit_shortcuts = IPython.keyboard_manager.edit_shortcuts.help();
var help, shortcut;
var i, half, n;
var element = $('<div/>');
// The documentation
var doc = $('<div/>').addClass('alert');
doc.append(
$('<button/>').addClass('close').attr('data-dismiss','alert').html('&times')
).append(
'The IPython Notebook has two different keyboard input modes. <b>Edit mode</b> '+
'allow you the type code/text into a cell and is indicated by a green cell '+
'border. <b>Command mode</b> binds the keyboard to notebook level actions '+
'and is indicated by a grey cell border.'
)
element.append(doc);
// Command mode
var cmd_div = this.build_command_help();
element.append(cmd_div);
// Edit mode
var edit_div = this.build_edit_help();
element.append(edit_div);
this.shortcut_dialog = IPython.dialog.modal({
title : "Keyboard shortcuts",
body : body,
body : element,
destroy : false,
buttons : {
Close : {}
@ -68,6 +59,72 @@ var IPython = (function (IPython) {
});
};
QuickHelp.prototype.build_command_help = function () {
var command_shortcuts = IPython.keyboard_manager.command_shortcuts.help();
var help, shortcut;
var i, half, n;
// Command mode
var cmd_div = $('<div/>').append($('<h4>Command Mode (press ESC to enable)</h4>'));
var cmd_sub_div = $('<div/>').addClass('hbox');
var cmd_col1 = $('<div/>').addClass('box-flex0');
var cmd_col2 = $('<div/>').addClass('box-flex0');
n = command_shortcuts.length;
half = ~~(n/2); // Truncate :)
for (i=0; i<half; i++) {
help = command_shortcuts[i]['help'];
shortcut = command_shortcuts[i]['shortcut'];
cmd_col1.append($('<div>').addClass('quickhelp').
append($('<span/>').addClass('shortcut_key').html(shortcut)).
append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
);
};
for (i=half; i<n; i++) {
help = command_shortcuts[i]['help'];
shortcut = command_shortcuts[i]['shortcut'];
cmd_col2.append($('<div>').addClass('quickhelp').
append($('<span/>').addClass('shortcut_key').html(shortcut)).
append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
);
};
cmd_sub_div.append(cmd_col1).append(cmd_col2);
cmd_div.append(cmd_sub_div);
return cmd_div;
}
QuickHelp.prototype.build_edit_help = function () {
var edit_shortcuts = IPython.keyboard_manager.edit_shortcuts.help();
var help, shortcut;
var i, half, n;
// Edit mode
var edit_div = $('<div/>').append($('<h4>Edit Mode (press ENTER to enable)</h4>'));
var edit_sub_div = $('<div/>').addClass('hbox');
var edit_col1 = $('<div/>').addClass('box-flex0');
var edit_col2 = $('<div/>').addClass('box-flex0');
n = edit_shortcuts.length;
half = ~~(n/2); // Truncate :)
for (i=0; i<half; i++) {
help = edit_shortcuts[i]['help'];
shortcut = edit_shortcuts[i]['shortcut'];
edit_col1.append($('<div>').addClass('quickhelp').
append($('<span/>').addClass('shortcut_key').html(shortcut)).
append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
);
};
for (i=half; i<n; i++) {
help = edit_shortcuts[i]['help'];
shortcut = edit_shortcuts[i]['shortcut'];
edit_col2.append($('<div>').addClass('quickhelp').
append($('<span/>').addClass('shortcut_key').html(shortcut)).
append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
);
};
edit_sub_div.append(edit_col1).append(edit_col2);
edit_div.append(edit_sub_div);
return edit_div;
}
// Set module variables
IPython.QuickHelp = QuickHelp;

View File

@ -41,7 +41,7 @@ var IPython = (function (IPython) {
// we cannot put this as a class key as it has handle to "this".
var cm_overwrite_options = {
onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
onKeyEvent: $.proxy(this.handle_keyevent,this)
};
options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
@ -64,7 +64,6 @@ var IPython = (function (IPython) {
};
/**
* Create the DOM element of the TextCell
* @method create_element
@ -101,19 +100,26 @@ var IPython = (function (IPython) {
TextCell.prototype.bind_events = function () {
IPython.Cell.prototype.bind_events.apply(this);
var that = this;
this.element.keydown(function (event) {
if (event.which === 13 && !event.shiftKey) {
if (that.rendered) {
that.edit();
return false;
};
};
});
this.element.dblclick(function () {
that.edit();
if (that.selected === false) {
$([IPython.events]).trigger('select.Cell', {'cell':that});
};
$([IPython.events]).trigger('edit_mode.Cell', {cell: that});
});
};
TextCell.prototype.handle_keyevent = function (editor, event) {
// console.log('CM', this.mode, event.which, event.type)
if (this.mode === 'command') {
return true;
} else if (this.mode === 'edit') {
return this.handle_codemirror_keyevent(editor, event);
}
};
/**
* This method gets called in CodeMirror's onKeyDown/onKeyPress
* handlers and is used to provide custom key handling.
@ -126,65 +132,86 @@ var IPython = (function (IPython) {
* @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
*/
TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
var that = this;
if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) {
// Always ignore shift-enter in CodeMirror as we handle it.
return true;
} 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()) {
event.stop();
return false;
} else {
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()) {
event.stop();
return false;
} else {
return true;
};
} else if (event.which === key.ESC && event.type === 'keydown') {
if (that.code_mirror.options.keyMap === "vim-insert") {
// vim keyMap is active and in insert mode. In this case we leave vim
// insert mode, but remain in notebook edit mode.
// Let' CM handle this event and prevent global handling.
event.stop();
return false;
} else {
// vim keyMap is not active. Leave notebook edit mode.
// Don't let CM handle the event, defer to global handling.
return true;
}
}
return false;
};
/**
* Select the current cell and trigger 'focus'
* @method select
*/
// Cell level actions
TextCell.prototype.select = function () {
IPython.Cell.prototype.select.apply(this);
var output = this.element.find("div.text_cell_render");
output.trigger('focus');
var cont = IPython.Cell.prototype.select.apply(this);
if (cont) {
if (this.mode === 'edit') {
this.code_mirror.refresh();
}
};
return cont;
};
/**
* unselect the current cell and `render` it
* @method unselect
*/
TextCell.prototype.unselect = function() {
// render on selection of another cell
this.render();
IPython.Cell.prototype.unselect.apply(this);
};
/**
*
* put the current cell in edition mode
* @method edit
*/
TextCell.prototype.edit = function () {
if (this.rendered === true) {
TextCell.prototype.unrender = function () {
if (this.read_only) return;
var cont = IPython.Cell.prototype.unrender.apply(this);
if (cont) {
var text_cell = this.element;
var output = text_cell.find("div.text_cell_render");
output.hide();
text_cell.find('div.text_cell_input').show();
this.code_mirror.refresh();
this.code_mirror.focus();
// We used to need an additional refresh() after the focus, but
// it appears that this has been fixed in CM. This bug would show
// up on FF when a newly loaded markdown cell was edited.
this.rendered = false;
if (this.get_text() === this.placeholder) {
this.set_text('');
this.refresh();
}
}
};
return cont;
};
TextCell.prototype.execute = function () {
this.render();
};
/**
* Empty, Subclasses must define render.
* @method render
*/
TextCell.prototype.render = function () {};
TextCell.prototype.edit_mode = function () {
var cont = IPython.Cell.prototype.edit_mode.apply(this);
if (cont) {
this.unrender();
this.focus_editor();
};
return cont;
}
/**
* setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
@ -222,36 +249,37 @@ var IPython = (function (IPython) {
};
/**
* not deprecated, but implementation wrong
* @method at_top
* @deprecated
* @return {Boolean} true is cell rendered, false otherwise
* I doubt this is what it is supposed to do
* this implementation is completly false
* @return {Boolean}
*/
TextCell.prototype.at_top = function () {
if (this.rendered) {
return true;
} else {
return false;
}
var cursor = this.code_mirror.getCursor();
if (cursor.line === 0 && cursor.ch === 0) {
return true;
} else {
return false;
};
};
};
/**
* not deprecated, but implementation wrong
* @method at_bottom
* @deprecated
* @return {Boolean} true is cell rendered, false otherwise
* I doubt this is what it is supposed to do
* this implementation is completly false
* @return {Boolean}
* */
TextCell.prototype.at_bottom = function () {
if (this.rendered) {
return true;
} else {
return false;
}
var cursor = this.code_mirror.getCursor();
if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
return true;
} else {
return false;
};
};
};
/**
@ -306,16 +334,14 @@ var IPython = (function (IPython) {
placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
}
MarkdownCell.prototype = new TextCell();
/**
* @method render
*/
MarkdownCell.prototype.render = function () {
if (this.rendered === false) {
var cont = IPython.TextCell.prototype.render.apply(this);
if (cont) {
var text = this.get_text();
var math = null;
if (text === "") { text = this.placeholder; }
@ -337,9 +363,9 @@ var IPython = (function (IPython) {
}
this.element.find('div.text_cell_input').hide();
this.element.find("div.text_cell_render").show();
this.typeset();
this.rendered = true;
}
this.typeset()
};
return cont;
};
@ -351,15 +377,12 @@ var IPython = (function (IPython) {
* @extends IPython.TextCell
*/
var RawCell = function (options) {
options = this.mergeopt(RawCell, options);
this.cell_type = 'raw';
TextCell.apply(this, [options]);
var that = this;
this.element.focusout(
function() { that.auto_highlight(); }
);
options = this.mergeopt(RawCell,options)
TextCell.apply(this, [options]);
this.cell_type = 'raw';
// RawCell should always hide its rendered div
this.element.find('div.text_cell_render').hide();
};
RawCell.options_default = {
@ -368,10 +391,17 @@ var IPython = (function (IPython) {
"When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
};
RawCell.prototype = new TextCell();
/** @method bind_events **/
RawCell.prototype.bind_events = function () {
TextCell.prototype.bind_events.apply(this);
var that = this
this.element.focusout(function() {
that.auto_highlight();
});
};
/**
* Trigger autodetection of highlight scheme for current cell
* @method auto_highlight
@ -382,68 +412,16 @@ var IPython = (function (IPython) {
/** @method render **/
RawCell.prototype.render = function () {
this.rendered = true;
// Make sure that this cell type can never be rendered
if (this.rendered) {
this.unrender();
}
var text = this.get_text();
if (text === "") { text = this.placeholder; }
console.log('rendering', text);
this.set_text(text);
};
/** @method handle_codemirror_keyevent **/
RawCell.prototype.handle_codemirror_keyevent = function (editor, event) {
var that = this;
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()) {
event.stop();
return false;
} else {
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()) {
event.stop();
return false;
} else {
return true;
};
};
return false;
};
/** @method select **/
RawCell.prototype.select = function () {
IPython.Cell.prototype.select.apply(this);
this.edit();
};
/** @method at_top **/
RawCell.prototype.at_top = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === 0 && cursor.ch === 0) {
return true;
} else {
return false;
}
};
/** @method at_bottom **/
RawCell.prototype.at_bottom = function () {
var cursor = this.code_mirror.getCursor();
if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
return true;
} else {
return false;
}
};
/**
* @class HeadingCell
* @extends IPython.TextCell
@ -538,7 +516,8 @@ var IPython = (function (IPython) {
HeadingCell.prototype.render = function () {
if (this.rendered === false) {
var cont = IPython.TextCell.prototype.render.apply(this);
if (cont) {
var text = this.get_text();
var math = null;
// Markdown headings must be a single line
@ -564,8 +543,9 @@ var IPython = (function (IPython) {
this.typeset();
this.element.find('div.text_cell_input').hide();
this.element.find("div.text_cell_render").show();
this.rendered = true;
};
return cont;
};
IPython.TextCell = TextCell;

View File

@ -6,6 +6,14 @@ div.cell {
.corner-all;
border : thin @border_color solid;
}
&.edit_mode {
.corner-all;
border : thin green solid;
}
}
div.cell {
width: 100%;
padding: 5px 5px 5px 0px;
/* This acts as a spacer between cells, that is outside the border */

View File

@ -28,6 +28,8 @@ div#notebook {
padding: 5px 5px 15px 5px;
margin: 0px;
border-top: 1px solid @border_color;
outline: none;
.border-box-sizing();
}
div.ui-widget-content {

View File

@ -9,7 +9,3 @@
display: inline-block;
}
div.quickhelp {
float: left;
width: 50%;
}

View File

@ -58,7 +58,9 @@ input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto
.ansibgpurple{background-color:magenta;}
.ansibgcyan{background-color:cyan;}
.ansibggray{background-color:gray;}
div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
div.cell.edit_mode{border-radius:4px;border:thin green solid;}
div.cell{width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}
div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
div.prompt:empty{padding-top:0;padding-bottom:0;}

View File

@ -1439,7 +1439,9 @@ input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto
.ansibgpurple{background-color:magenta;}
.ansibgcyan{background-color:cyan;}
.ansibggray{background-color:gray;}
div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
div.cell.edit_mode{border-radius:4px;border:thin green solid;}
div.cell{width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}
div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
div.prompt:empty{padding-top:0;padding-bottom:0;}
@ -1535,7 +1537,7 @@ body{background-color:#ffffff;}
body.notebook_app{overflow:hidden;}
span#notebook_name{height:1em;line-height:1em;padding:3px;border:none;font-size:146.5%;}
div#notebook_panel{margin:0px 0px 0px 0px;padding:0px;-webkit-box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);}
div#notebook{overflow-y:scroll;overflow-x:auto;width:100%;padding:5px 5px 15px 5px;margin:0px;border-top:1px solid #ababab;}
div#notebook{overflow-y:scroll;overflow-x:auto;width:100%;padding:5px 5px 15px 5px;margin:0px;border-top:1px solid #ababab;outline:none;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;}
div.ui-widget-content{border:1px solid #ababab;outline:none;}
pre.dialog{background-color:#f7f7f7;border:1px solid #ddd;border-radius:4px;padding:0.4em;padding-left:2em;}
p.dialog{padding:0.2em;}
@ -1567,7 +1569,6 @@ div#pager_splitter{height:8px;}
div#pager{overflow:auto;display:none;}div#pager pre{font-size:13px;line-height:1.231em;color:#000000;background-color:#f7f7f7;padding:0.4em;}
.shortcut_key{display:inline-block;width:15ex;text-align:right;font-family:monospace;}
.shortcut_descr{display:inline-block;}
div.quickhelp{float:left;width:50%;}
span#save_widget{padding:0px 5px;margin-top:12px;}
span#checkpoint_status,span#autosave_status{font-size:small;}
@media (max-width:767px){span#save_widget{font-size:small;} span#checkpoint_status,span#autosave_status{font-size:x-small;}}@media (max-width:767px){span#checkpoint_status,span#autosave_status{display:none;}}@media (min-width:768px) and (max-width:979px){span#checkpoint_status{display:none;} span#autosave_status{font-size:x-small;}}.toolbar{padding:0px 10px;margin-top:-5px;}.toolbar select,.toolbar label{width:auto;height:26px;vertical-align:middle;margin-right:2px;margin-bottom:0px;display:inline;font-size:92%;margin-left:0.3em;margin-right:0.3em;padding:0px;padding-top:3px;}

View File

@ -140,8 +140,10 @@ class="notebook_app"
<ul class="dropdown-menu">
<li id="run_cell" title="Run this cell, and move cursor to the next one">
<a href="#">Run</a></li>
<li id="run_cell_in_place" title="Run this cell, without moving to the next one">
<a href="#">Run in Place</a></li>
<li id="run_cell_select_below" title="Run this cell, select below">
<a href="#">Run and Select Below</a></li>
<li id="run_cell_insert_below" title="Run this cell, insert below">
<a href="#">Run and Insert Below</a></li>
<li id="run_all_cells" title="Run all cells in the notebook">
<a href="#">Run All</a></li>
<li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
@ -291,6 +293,7 @@ class="notebook_app"
<script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
<script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>

View File

@ -31,11 +31,13 @@ casper.notebook_test(function () {
var result = this.get_output_cell(0);
var num_cells = this.get_cells_length();
this.test.assertEquals(result.text, '11\n', 'cell execute (using ctrl-enter)');
this.test.assertEquals(num_cells, 1, ' ^--- does not add a new cell')
this.test.assertEquals(num_cells, 2, 'ctrl-enter adds a new cell at the bottom')
});
// do it again with the keyboard shortcut
this.thenEvaluate(function () {
IPython.notebook.select(1);
IPython.notebook.delete_cell();
var cell = IPython.notebook.get_cell(0);
cell.set_text('a=12; print(a)');
cell.clear_output();
@ -48,7 +50,7 @@ casper.notebook_test(function () {
var result = this.get_output_cell(0);
var num_cells = this.get_cells_length();
this.test.assertEquals(result.text, '12\n', 'cell execute (using shift-enter)');
this.test.assertEquals(num_cells, 2, ' ^--- adds a new cell')
this.test.assertEquals(num_cells, 1, 'shift-enter adds no new cell at the bottom')
});
// press the "play" triangle button in the toolbar

View File

@ -4,11 +4,12 @@
casper.notebook_test(function() {
var output = this.evaluate(function () {
// Fill in test data.
IPython.notebook.command_mode();
var set_cell_text = function () {
var cell_one = IPython.notebook.get_selected_cell();
cell_one.set_text('a = 5');
IPython.notebook.insert_cell_below('code');
IPython.utils.press(IPython.keycodes.b)
var cell_two = IPython.notebook.get_selected_cell();
cell_two.set_text('print(a)');
};
@ -31,7 +32,7 @@ casper.notebook_test(function() {
});
this.test.assertEquals(output.above, 'a = 5\nprint(a)',
'Successful insert_cell_above().');
'Successful merge_cell_above().');
this.test.assertEquals(output.below, 'a = 5\nprint(a)',
'Successful insert_cell_below().');
'Successful merge_cell_below().');
});

View File

@ -0,0 +1,421 @@
{
"metadata": {
"name": ""
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"User Interface"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This notebook describes the user interface of the IPython Notebook. This includes both mouse and keyboard based navigation and interaction.\n",
"\n",
"<div class=\"alert\" style=\"margin: 10px\">\n",
"As of IPython 2.0, the user interface has changed significantly. Because of this we highly recommend existing users to review this information after upgrading to IPython 2.0. All new users of IPython should review this information as well.\n",
"</div>"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Modal editor"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Starting with IPython 2.0, the IPython Notebook has a modal user interface. This means that the keyboard does different things depending on which mode the Notebook is in. There are two modes: edit mode and command mode."
]
},
{
"cell_type": "heading",
"level": 3,
"metadata": {},
"source": [
"Edit mode"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Edit mode is indicated by a green cell border and a prompt showing in the editor area:\n",
"\n",
"<img src=\"images/edit_mode.png\">\n",
"\n",
"When a cell is in edit mode, you can type into the cell, like a normal text editor.\n",
"\n",
"<div class=\"alert alert-success\" style=\"margin: 10px\">\n",
"Enter edit mode by pressing `enter` or using the mouse to click on a cell's editor area.\n",
"</div>"
]
},
{
"cell_type": "heading",
"level": 3,
"metadata": {},
"source": [
"Command mode"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Command mode is indicated by a grey cell border:\n",
"\n",
"<img src=\"images/command_mode.png\">\n",
"\n",
"When you are in command mode, you are able to edit the notebook as a whole, but not type into individual cells. Most importantly, in command mode, the keyboard is mapped to a set of shortcuts that let you perform notebook and cell actions efficiently. For example, if you are in command mode and you press `c`, you will copy the current cell - no modifier is needed.\n",
"\n",
"<div class=\"alert alert-error\" style=\"margin: 10px\">\n",
"Don't try to type into a cell in command mode; unexpected things will happen!\n",
"</div>\n",
"\n",
"<div class=\"alert alert-success\" style=\"margin: 10px\">\n",
"Enter command mode by pressing `esc` or using the mouse to click *outside* a cell's editor area.\n",
"</div>"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Mouse navigation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"All navigation and actions in the Notebook are available using the mouse through the menubar and toolbar, which are both above the main Notebook area:\n",
"\n",
"<img src=\"images/menubar_toolbar.png\">"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The first idea of mouse based navigation is that **cells can be selected by clicking on them.** The currently selected cell gets a grey or green border depending on whether the notebook is in edit or command mode. If you click inside a cell's editor area, you will enter edit mode. If you click on the prompt or output area of a cell you will enter command mode.\n",
"\n",
"If you are running this notebook in a live session (not on http://nbviewer.ipython.org) try selecting different cells and going between edit and command mode. Try typing into a cell."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The second idea of mouse based navigation is that **cell actions usually apply to the currently selected cell**. Thus if you want to run the code in a cell, you would select it and click the \"Play\" button in the toolbar or the \"Cell:Run\" menu item. Similarly, to copy a cell you would select it and click the \"Copy\" button in the toolbar or the \"Edit:Copy\" menu item. With this simple pattern, you should be able to do most everything you need with the mouse.\n",
"\n",
"Markdown and heading cells have one other state that can be modified with the mouse. These cells can either be rendered or unrendered. When they are rendered, you will see a nice formatted representation of the cell's contents. When they are unrendered, you will see the raw text source of the cell. To render the selected cell with the mouse, click the \"Play\" button in the toolbar or the \"Cell:Run\" menu item. To unrender the selected cell, double click on the cell."
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Keyboard Navigation"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The modal user interface of the IPython Notebook has been optimized for efficient keyboard usage. This is made possible by having two different sets of keyboard shortcuts: one set that is active in edit mode and another in command mode.\n",
"\n",
"The most important keyboard shortcuts are `enter`, which enters edit mode, and `esc`, which enters command mode.\n",
"\n",
"In edit mode, most of the keyboard is dedicated to typing into the cell's editor. Thus, in edit mode there are relatively few shortcuts:"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `display_edit_shortcuts()` function used here is defined in the [Utilities section](#Utilities) at the bottom of this notebook."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"display_edit_shortcuts()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div class=\"hbox\"><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">esc</span><span class=\"shortcut_descr\"> : command mode</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+m</span><span class=\"shortcut_descr\"> : command mode</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+enter</span><span class=\"shortcut_descr\"> : run cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+enter</span><span class=\"shortcut_descr\"> : run cell, select below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">alt+enter</span><span class=\"shortcut_descr\"> : run cell, insert below</span></div></div><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">up</span><span class=\"shortcut_descr\"> : select previous cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">down</span><span class=\"shortcut_descr\"> : select next cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">alt+-</span><span class=\"shortcut_descr\"> : split cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">meta+s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+s</span><span class=\"shortcut_descr\"> : save notebook</span></div></div></div>"
],
"output_type": "display_data"
},
{
"javascript": [
"var help = IPython.quick_help.build_edit_help();\n",
"help.children().first().remove();\n",
"this.append_output({output_type: 'display_data', html: help.html()});"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<IPython.core.display.Javascript at 0x10e441250>"
]
}
],
"prompt_number": 14
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are two other keyboard shortcuts in edit mode that are not listed here:\n",
"\n",
"* `tab`: trigger \"tab\" completion\n",
"* `shift+tab`: open the tooltip"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In command mode, the entire keyboard is available for shortcuts:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"display_command_shortcuts()"
],
"language": "python",
"metadata": {},
"outputs": [
{
"html": [
"<div class=\"hbox\"><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">enter</span><span class=\"shortcut_descr\"> : edit mode</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+enter</span><span class=\"shortcut_descr\"> : run cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+enter</span><span class=\"shortcut_descr\"> : run cell, select below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">alt+enter</span><span class=\"shortcut_descr\"> : run cell, insert below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">y</span><span class=\"shortcut_descr\"> : to code</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">m</span><span class=\"shortcut_descr\"> : to markdown</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">t</span><span class=\"shortcut_descr\"> : to raw</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">1</span><span class=\"shortcut_descr\"> : to heading 1</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">2</span><span class=\"shortcut_descr\"> : to heading 2</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">3</span><span class=\"shortcut_descr\"> : to heading 3</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">4</span><span class=\"shortcut_descr\"> : to heading 4</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">5</span><span class=\"shortcut_descr\"> : to heading 5</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">6</span><span class=\"shortcut_descr\"> : to heading 6</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">up</span><span class=\"shortcut_descr\"> : select previous cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">down</span><span class=\"shortcut_descr\"> : select next cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">k</span><span class=\"shortcut_descr\"> : select previous cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">j</span><span class=\"shortcut_descr\"> : select next cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+k</span><span class=\"shortcut_descr\"> : move cell up</span></div></div><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+j</span><span class=\"shortcut_descr\"> : move cell down</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">a</span><span class=\"shortcut_descr\"> : insert cell above</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">b</span><span class=\"shortcut_descr\"> : insert cell below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">x</span><span class=\"shortcut_descr\"> : cut cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">c</span><span class=\"shortcut_descr\"> : copy cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">v</span><span class=\"shortcut_descr\"> : paste cell below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">z</span><span class=\"shortcut_descr\"> : undo last delete</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">d</span><span class=\"shortcut_descr\"> : delete cell (press twice)</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+=</span><span class=\"shortcut_descr\"> : merge cell below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">meta+s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">l</span><span class=\"shortcut_descr\"> : toggle line numbers</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">o</span><span class=\"shortcut_descr\"> : toggle output</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+o</span><span class=\"shortcut_descr\"> : toggle output</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">h</span><span class=\"shortcut_descr\"> : keyboard shortcuts</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">i</span><span class=\"shortcut_descr\"> : interrupt kernel</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">.</span><span class=\"shortcut_descr\"> : restart kernel</span></div></div></div>"
],
"output_type": "display_data"
},
{
"javascript": [
"var help = IPython.quick_help.build_command_help();\n",
"help.children().first().remove();\n",
"this.append_output({output_type: 'display_data', html: help.html()});"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<IPython.core.display.Javascript at 0x10e441410>"
]
}
],
"prompt_number": 15
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Here the rough order in which we recommend learning the command mode shortcuts:\n",
"\n",
"1. Basic navigation: `enter`, `shift-enter`, `up/k`, `down/j`\n",
"2. Saving the notebook: `s`\n",
"2. Cell types: `y`, `m`, `1-6`, `t`\n",
"3. Cell creation and movement: `a`, `b`, `ctrl+k`, `ctrl+j`\n",
"4. Cell editing: `x`, `c`, `v`, `d`, `z`, `shift+=`\n",
"5. Kernel operations: `i`, `.`"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Keyboard shortcut customization"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Starting with IPython 2.0 keyboard shortcuts in command and edit mode are fully customizable. These customizations are made using the IPython JavaScript API. Here is an example that makes the `r` key available for running a cell:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%javascript\n",
"\n",
"IPython.keyboard_manager.command_shortcuts.add_shortcut('r', {\n",
" help : 'run cell',\n",
" help_index : 'zz',\n",
" handler : function (event) {\n",
" IPython.notebook.execute_cell();\n",
" return false;\n",
" }}\n",
");"
],
"language": "python",
"metadata": {},
"outputs": [
{
"javascript": [
"\n",
"IPython.keyboard_manager.command_shortcuts.add_shortcut('r', {\n",
" help : 'run cell',\n",
" help_index : 'aa',\n",
" handler : function (event) {\n",
" IPython.notebook.execute_cell();\n",
" return false;\n",
" }}\n",
");"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<IPython.core.display.Javascript at 0x1019ba990>"
]
}
],
"prompt_number": 6
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"There are a couple of points to mention about this API:\n",
"\n",
"* The `help_index` field is used to sort the shortcuts in the Keyboard Shortcuts help dialog. It defaults to `zz`.\n",
"* When a handler returns `false` it indicates that the event should stop propagating and the default action should not be performed. For further details about the `event` object or event handling, see the jQuery docs.\n",
"* If you don't need a `help` or `help_index` field, you can simply pass a function as the second argument to `add_shortcut`."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%javascript\n",
"\n",
"IPython.keyboard_manager.command_shortcuts.add_shortcut('r', function (event) {\n",
" IPython.notebook.execute_cell();\n",
" return false;\n",
"});"
],
"language": "python",
"metadata": {},
"outputs": [
{
"javascript": [
"\n",
"IPython.keyboard_manager.command_shortcuts.add_shortcut('r', function (event) {\n",
" IPython.notebook.execute_cell();\n",
" return false;\n",
"});"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<IPython.core.display.Javascript at 0x1019baf90>"
]
}
],
"prompt_number": 11
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Likewise, to remove a shortcut, use `remove_shortcut`:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%javascript\n",
"\n",
"IPython.keyboard_manager.command_shortcuts.remove_shortcut('r');"
],
"language": "python",
"metadata": {},
"outputs": [
{
"javascript": [
"\n",
"IPython.keyboard_manager.command_shortcuts.remove_shortcut('r');"
],
"metadata": {},
"output_type": "display_data",
"text": [
"<IPython.core.display.Javascript at 0x1019ba950>"
]
}
],
"prompt_number": 8
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"If you want your keyboard shortcuts to be active for all of your notebooks, put the above API calls into your `custom.js` file."
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Utilities"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We use the following functions to generate the keyboard shortcut listings above."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"from IPython.display import Javascript, display\n",
"\n",
"t = \"\"\"var help = IPython.quick_help.build_{0}_help();\n",
"help.children().first().remove();\n",
"this.append_output({{output_type: 'display_data', html: help.html()}});\"\"\"\n",
"\n",
"def display_command_shortcuts():\n",
" display(Javascript(t.format('command')))\n",
"\n",
"def display_edit_shortcuts():\n",
" display(Javascript(t.format('edit'))) "
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
}
],
"metadata": {}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB