##// END OF EJS Templates
Don't autoclose strings...
MinRK -
Show More
@@ -1,579 +1,580 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11 /**
11 /**
12 * An extendable module that provide base functionnality to create cell for notebook.
12 * An extendable module that provide base functionnality to create cell for notebook.
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule CodeCell
15 * @submodule CodeCell
16 */
16 */
17
17
18
18
19 /* local util for codemirror */
19 /* local util for codemirror */
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
21
21
22 /**
22 /**
23 *
23 *
24 * function to delete until previous non blanking space character
24 * function to delete until previous non blanking space character
25 * or first multiple of 4 tabstop.
25 * or first multiple of 4 tabstop.
26 * @private
26 * @private
27 */
27 */
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
30 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
30 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
32 var tabsize = cm.getOption('tabSize');
32 var tabsize = cm.getOption('tabSize');
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
34 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
34 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
35 var select = cm.getRange(from,cur);
35 var select = cm.getRange(from,cur);
36 if( select.match(/^\ +$/) !== null){
36 if( select.match(/^\ +$/) !== null){
37 cm.replaceRange("",from,cur);
37 cm.replaceRange("",from,cur);
38 } else {
38 } else {
39 cm.deleteH(-1,"char");
39 cm.deleteH(-1,"char");
40 }
40 }
41 };
41 };
42
42
43
43
44 var IPython = (function (IPython) {
44 var IPython = (function (IPython) {
45 "use strict";
45 "use strict";
46
46
47 var utils = IPython.utils;
47 var utils = IPython.utils;
48 var keycodes = IPython.keyboard.keycodes;
48 var keycodes = IPython.keyboard.keycodes;
49
49
50 /**
50 /**
51 * A Cell conceived to write code.
51 * A Cell conceived to write code.
52 *
52 *
53 * The kernel doesn't have to be set at creation time, in that case
53 * The kernel doesn't have to be set at creation time, in that case
54 * it will be null and set_kernel has to be called later.
54 * it will be null and set_kernel has to be called later.
55 * @class CodeCell
55 * @class CodeCell
56 * @extends IPython.Cell
56 * @extends IPython.Cell
57 *
57 *
58 * @constructor
58 * @constructor
59 * @param {Object|null} kernel
59 * @param {Object|null} kernel
60 * @param {object|undefined} [options]
60 * @param {object|undefined} [options]
61 * @param [options.cm_config] {object} config to pass to CodeMirror
61 * @param [options.cm_config] {object} config to pass to CodeMirror
62 */
62 */
63 var CodeCell = function (kernel, options) {
63 var CodeCell = function (kernel, options) {
64 this.kernel = kernel || null;
64 this.kernel = kernel || null;
65 this.collapsed = false;
65 this.collapsed = false;
66
66
67 // create all attributed in constructor function
67 // create all attributed in constructor function
68 // even if null for V8 VM optimisation
68 // even if null for V8 VM optimisation
69 this.input_prompt_number = null;
69 this.input_prompt_number = null;
70 this.celltoolbar = null;
70 this.celltoolbar = null;
71 this.output_area = null;
71 this.output_area = null;
72 this.last_msg_id = null;
72 this.last_msg_id = null;
73 this.completer = null;
73 this.completer = null;
74
74
75
75
76 var cm_overwrite_options = {
76 var cm_overwrite_options = {
77 onKeyEvent: $.proxy(this.handle_keyevent,this)
77 onKeyEvent: $.proxy(this.handle_keyevent,this)
78 };
78 };
79
79
80 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
80 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
81
81
82 IPython.Cell.apply(this,[options]);
82 IPython.Cell.apply(this,[options]);
83
83
84 // Attributes we want to override in this subclass.
84 // Attributes we want to override in this subclass.
85 this.cell_type = "code";
85 this.cell_type = "code";
86
86
87 var that = this;
87 var that = this;
88 this.element.focusout(
88 this.element.focusout(
89 function() { that.auto_highlight(); }
89 function() { that.auto_highlight(); }
90 );
90 );
91 };
91 };
92
92
93 CodeCell.options_default = {
93 CodeCell.options_default = {
94 cm_config : {
94 cm_config : {
95 extraKeys: {
95 extraKeys: {
96 "Tab" : "indentMore",
96 "Tab" : "indentMore",
97 "Shift-Tab" : "indentLess",
97 "Shift-Tab" : "indentLess",
98 "Backspace" : "delSpaceToPrevTabStop",
98 "Backspace" : "delSpaceToPrevTabStop",
99 "Cmd-/" : "toggleComment",
99 "Cmd-/" : "toggleComment",
100 "Ctrl-/" : "toggleComment"
100 "Ctrl-/" : "toggleComment"
101 },
101 },
102 mode: 'ipython',
102 mode: 'ipython',
103 theme: 'ipython',
103 theme: 'ipython',
104 matchBrackets: true,
104 matchBrackets: true,
105 autoCloseBrackets: true
105 // don't auto-close strings because of CodeMirror #2385
106 autoCloseBrackets: "()[]{}"
106 }
107 }
107 };
108 };
108
109
109 CodeCell.msg_cells = {};
110 CodeCell.msg_cells = {};
110
111
111 CodeCell.prototype = new IPython.Cell();
112 CodeCell.prototype = new IPython.Cell();
112
113
113 /**
114 /**
114 * @method auto_highlight
115 * @method auto_highlight
115 */
116 */
116 CodeCell.prototype.auto_highlight = function () {
117 CodeCell.prototype.auto_highlight = function () {
117 this._auto_highlight(IPython.config.cell_magic_highlight);
118 this._auto_highlight(IPython.config.cell_magic_highlight);
118 };
119 };
119
120
120 /** @method create_element */
121 /** @method create_element */
121 CodeCell.prototype.create_element = function () {
122 CodeCell.prototype.create_element = function () {
122 IPython.Cell.prototype.create_element.apply(this, arguments);
123 IPython.Cell.prototype.create_element.apply(this, arguments);
123
124
124 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
125 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
125 cell.attr('tabindex','2');
126 cell.attr('tabindex','2');
126
127
127 var input = $('<div></div>').addClass('input');
128 var input = $('<div></div>').addClass('input');
128 var prompt = $('<div/>').addClass('prompt input_prompt');
129 var prompt = $('<div/>').addClass('prompt input_prompt');
129 var inner_cell = $('<div/>').addClass('inner_cell');
130 var inner_cell = $('<div/>').addClass('inner_cell');
130 this.celltoolbar = new IPython.CellToolbar(this);
131 this.celltoolbar = new IPython.CellToolbar(this);
131 inner_cell.append(this.celltoolbar.element);
132 inner_cell.append(this.celltoolbar.element);
132 var input_area = $('<div/>').addClass('input_area');
133 var input_area = $('<div/>').addClass('input_area');
133 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
134 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
134 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
135 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
135 inner_cell.append(input_area);
136 inner_cell.append(input_area);
136 input.append(prompt).append(inner_cell);
137 input.append(prompt).append(inner_cell);
137
138
138 var widget_area = $('<div/>')
139 var widget_area = $('<div/>')
139 .addClass('widget-area')
140 .addClass('widget-area')
140 .hide();
141 .hide();
141 this.widget_area = widget_area;
142 this.widget_area = widget_area;
142 var widget_prompt = $('<div/>')
143 var widget_prompt = $('<div/>')
143 .addClass('prompt')
144 .addClass('prompt')
144 .appendTo(widget_area);
145 .appendTo(widget_area);
145 var widget_subarea = $('<div/>')
146 var widget_subarea = $('<div/>')
146 .addClass('widget-subarea')
147 .addClass('widget-subarea')
147 .appendTo(widget_area);
148 .appendTo(widget_area);
148 this.widget_subarea = widget_subarea;
149 this.widget_subarea = widget_subarea;
149 var widget_clear_buton = $('<button />')
150 var widget_clear_buton = $('<button />')
150 .addClass('close')
151 .addClass('close')
151 .html('&times;')
152 .html('&times;')
152 .click(function() {
153 .click(function() {
153 widget_area.slideUp('', function(){ widget_subarea.html(''); });
154 widget_area.slideUp('', function(){ widget_subarea.html(''); });
154 })
155 })
155 .appendTo(widget_prompt);
156 .appendTo(widget_prompt);
156
157
157 var output = $('<div></div>');
158 var output = $('<div></div>');
158 cell.append(input).append(widget_area).append(output);
159 cell.append(input).append(widget_area).append(output);
159 this.element = cell;
160 this.element = cell;
160 this.output_area = new IPython.OutputArea(output, true);
161 this.output_area = new IPython.OutputArea(output, true);
161 this.completer = new IPython.Completer(this);
162 this.completer = new IPython.Completer(this);
162 };
163 };
163
164
164 /** @method bind_events */
165 /** @method bind_events */
165 CodeCell.prototype.bind_events = function () {
166 CodeCell.prototype.bind_events = function () {
166 IPython.Cell.prototype.bind_events.apply(this);
167 IPython.Cell.prototype.bind_events.apply(this);
167 var that = this;
168 var that = this;
168
169
169 this.element.focusout(
170 this.element.focusout(
170 function() { that.auto_highlight(); }
171 function() { that.auto_highlight(); }
171 );
172 );
172 };
173 };
173
174
174 CodeCell.prototype.handle_keyevent = function (editor, event) {
175 CodeCell.prototype.handle_keyevent = function (editor, event) {
175
176
176 // console.log('CM', this.mode, event.which, event.type)
177 // console.log('CM', this.mode, event.which, event.type)
177
178
178 if (this.mode === 'command') {
179 if (this.mode === 'command') {
179 return true;
180 return true;
180 } else if (this.mode === 'edit') {
181 } else if (this.mode === 'edit') {
181 return this.handle_codemirror_keyevent(editor, event);
182 return this.handle_codemirror_keyevent(editor, event);
182 }
183 }
183 };
184 };
184
185
185 /**
186 /**
186 * This method gets called in CodeMirror's onKeyDown/onKeyPress
187 * This method gets called in CodeMirror's onKeyDown/onKeyPress
187 * handlers and is used to provide custom key handling. Its return
188 * handlers and is used to provide custom key handling. Its return
188 * value is used to determine if CodeMirror should ignore the event:
189 * value is used to determine if CodeMirror should ignore the event:
189 * true = ignore, false = don't ignore.
190 * true = ignore, false = don't ignore.
190 * @method handle_codemirror_keyevent
191 * @method handle_codemirror_keyevent
191 */
192 */
192 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
193 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
193
194
194 var that = this;
195 var that = this;
195 // whatever key is pressed, first, cancel the tooltip request before
196 // whatever key is pressed, first, cancel the tooltip request before
196 // they are sent, and remove tooltip if any, except for tab again
197 // they are sent, and remove tooltip if any, except for tab again
197 var tooltip_closed = null;
198 var tooltip_closed = null;
198 if (event.type === 'keydown' && event.which != keycodes.tab ) {
199 if (event.type === 'keydown' && event.which != keycodes.tab ) {
199 tooltip_closed = IPython.tooltip.remove_and_cancel_tooltip();
200 tooltip_closed = IPython.tooltip.remove_and_cancel_tooltip();
200 }
201 }
201
202
202 var cur = editor.getCursor();
203 var cur = editor.getCursor();
203 if (event.keyCode === keycodes.enter){
204 if (event.keyCode === keycodes.enter){
204 this.auto_highlight();
205 this.auto_highlight();
205 }
206 }
206
207
207 if (event.keyCode === keycodes.enter && (event.shiftKey || event.ctrlKey || event.altKey)) {
208 if (event.keyCode === keycodes.enter && (event.shiftKey || event.ctrlKey || event.altKey)) {
208 // Always ignore shift-enter in CodeMirror as we handle it.
209 // Always ignore shift-enter in CodeMirror as we handle it.
209 return true;
210 return true;
210 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
211 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
211 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
212 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
212 // browser and keyboard layout !
213 // browser and keyboard layout !
213 // Pressing '(' , request tooltip, don't forget to reappend it
214 // Pressing '(' , request tooltip, don't forget to reappend it
214 // The second argument says to hide the tooltip if the docstring
215 // The second argument says to hide the tooltip if the docstring
215 // is actually empty
216 // is actually empty
216 IPython.tooltip.pending(that, true);
217 IPython.tooltip.pending(that, true);
217 } else if (event.which === keycodes.up && event.type === 'keydown') {
218 } else if (event.which === keycodes.up && event.type === 'keydown') {
218 // If we are not at the top, let CM handle the up arrow and
219 // If we are not at the top, let CM handle the up arrow and
219 // prevent the global keydown handler from handling it.
220 // prevent the global keydown handler from handling it.
220 if (!that.at_top()) {
221 if (!that.at_top()) {
221 event.stop();
222 event.stop();
222 return false;
223 return false;
223 } else {
224 } else {
224 return true;
225 return true;
225 }
226 }
226 } else if (event.which === keycodes.esc && event.type === 'keydown') {
227 } else if (event.which === keycodes.esc && event.type === 'keydown') {
227 // First see if the tooltip is active and if so cancel it.
228 // First see if the tooltip is active and if so cancel it.
228 if (tooltip_closed) {
229 if (tooltip_closed) {
229 // The call to remove_and_cancel_tooltip above in L177 doesn't pass
230 // The call to remove_and_cancel_tooltip above in L177 doesn't pass
230 // force=true. Because of this it won't actually close the tooltip
231 // force=true. Because of this it won't actually close the tooltip
231 // if it is in sticky mode. Thus, we have to check again if it is open
232 // if it is in sticky mode. Thus, we have to check again if it is open
232 // and close it with force=true.
233 // and close it with force=true.
233 if (!IPython.tooltip._hidden) {
234 if (!IPython.tooltip._hidden) {
234 IPython.tooltip.remove_and_cancel_tooltip(true);
235 IPython.tooltip.remove_and_cancel_tooltip(true);
235 }
236 }
236 // If we closed the tooltip, don't let CM or the global handlers
237 // If we closed the tooltip, don't let CM or the global handlers
237 // handle this event.
238 // handle this event.
238 event.stop();
239 event.stop();
239 return true;
240 return true;
240 }
241 }
241 if (that.code_mirror.options.keyMap === "vim-insert") {
242 if (that.code_mirror.options.keyMap === "vim-insert") {
242 // vim keyMap is active and in insert mode. In this case we leave vim
243 // vim keyMap is active and in insert mode. In this case we leave vim
243 // insert mode, but remain in notebook edit mode.
244 // insert mode, but remain in notebook edit mode.
244 // Let' CM handle this event and prevent global handling.
245 // Let' CM handle this event and prevent global handling.
245 event.stop();
246 event.stop();
246 return false;
247 return false;
247 } else {
248 } else {
248 // vim keyMap is not active. Leave notebook edit mode.
249 // vim keyMap is not active. Leave notebook edit mode.
249 // Don't let CM handle the event, defer to global handling.
250 // Don't let CM handle the event, defer to global handling.
250 return true;
251 return true;
251 }
252 }
252 } else if (event.which === keycodes.down && event.type === 'keydown') {
253 } else if (event.which === keycodes.down && event.type === 'keydown') {
253 // If we are not at the bottom, let CM handle the down arrow and
254 // If we are not at the bottom, let CM handle the down arrow and
254 // prevent the global keydown handler from handling it.
255 // prevent the global keydown handler from handling it.
255 if (!that.at_bottom()) {
256 if (!that.at_bottom()) {
256 event.stop();
257 event.stop();
257 return false;
258 return false;
258 } else {
259 } else {
259 return true;
260 return true;
260 }
261 }
261 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
262 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
262 if (editor.somethingSelected()){
263 if (editor.somethingSelected()){
263 var anchor = editor.getCursor("anchor");
264 var anchor = editor.getCursor("anchor");
264 var head = editor.getCursor("head");
265 var head = editor.getCursor("head");
265 if( anchor.line != head.line){
266 if( anchor.line != head.line){
266 return false;
267 return false;
267 }
268 }
268 }
269 }
269 IPython.tooltip.request(that);
270 IPython.tooltip.request(that);
270 event.stop();
271 event.stop();
271 return true;
272 return true;
272 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
273 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
273 // Tab completion.
274 // Tab completion.
274 IPython.tooltip.remove_and_cancel_tooltip();
275 IPython.tooltip.remove_and_cancel_tooltip();
275 if (editor.somethingSelected()) {
276 if (editor.somethingSelected()) {
276 return false;
277 return false;
277 }
278 }
278 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
279 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
279 if (pre_cursor.trim() === "") {
280 if (pre_cursor.trim() === "") {
280 // Don't autocomplete if the part of the line before the cursor
281 // Don't autocomplete if the part of the line before the cursor
281 // is empty. In this case, let CodeMirror handle indentation.
282 // is empty. In this case, let CodeMirror handle indentation.
282 return false;
283 return false;
283 } else {
284 } else {
284 event.stop();
285 event.stop();
285 this.completer.startCompletion();
286 this.completer.startCompletion();
286 return true;
287 return true;
287 }
288 }
288 } else {
289 } else {
289 // keypress/keyup also trigger on TAB press, and we don't want to
290 // keypress/keyup also trigger on TAB press, and we don't want to
290 // use those to disable tab completion.
291 // use those to disable tab completion.
291 return false;
292 return false;
292 }
293 }
293 return false;
294 return false;
294 };
295 };
295
296
296 // Kernel related calls.
297 // Kernel related calls.
297
298
298 CodeCell.prototype.set_kernel = function (kernel) {
299 CodeCell.prototype.set_kernel = function (kernel) {
299 this.kernel = kernel;
300 this.kernel = kernel;
300 };
301 };
301
302
302 /**
303 /**
303 * Execute current code cell to the kernel
304 * Execute current code cell to the kernel
304 * @method execute
305 * @method execute
305 */
306 */
306 CodeCell.prototype.execute = function () {
307 CodeCell.prototype.execute = function () {
307 this.output_area.clear_output();
308 this.output_area.clear_output();
308
309
309 // Clear widget area
310 // Clear widget area
310 this.widget_subarea.html('');
311 this.widget_subarea.html('');
311 this.widget_subarea.height('');
312 this.widget_subarea.height('');
312 this.widget_area.height('');
313 this.widget_area.height('');
313 this.widget_area.hide();
314 this.widget_area.hide();
314
315
315 this.set_input_prompt('*');
316 this.set_input_prompt('*');
316 this.element.addClass("running");
317 this.element.addClass("running");
317 if (this.last_msg_id) {
318 if (this.last_msg_id) {
318 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
319 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
319 }
320 }
320 var callbacks = this.get_callbacks();
321 var callbacks = this.get_callbacks();
321
322
322 var old_msg_id = this.last_msg_id;
323 var old_msg_id = this.last_msg_id;
323 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
324 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
324 if (old_msg_id) {
325 if (old_msg_id) {
325 delete CodeCell.msg_cells[old_msg_id];
326 delete CodeCell.msg_cells[old_msg_id];
326 }
327 }
327 CodeCell.msg_cells[this.last_msg_id] = this;
328 CodeCell.msg_cells[this.last_msg_id] = this;
328 };
329 };
329
330
330 /**
331 /**
331 * Construct the default callbacks for
332 * Construct the default callbacks for
332 * @method get_callbacks
333 * @method get_callbacks
333 */
334 */
334 CodeCell.prototype.get_callbacks = function () {
335 CodeCell.prototype.get_callbacks = function () {
335 return {
336 return {
336 shell : {
337 shell : {
337 reply : $.proxy(this._handle_execute_reply, this),
338 reply : $.proxy(this._handle_execute_reply, this),
338 payload : {
339 payload : {
339 set_next_input : $.proxy(this._handle_set_next_input, this),
340 set_next_input : $.proxy(this._handle_set_next_input, this),
340 page : $.proxy(this._open_with_pager, this)
341 page : $.proxy(this._open_with_pager, this)
341 }
342 }
342 },
343 },
343 iopub : {
344 iopub : {
344 output : $.proxy(this.output_area.handle_output, this.output_area),
345 output : $.proxy(this.output_area.handle_output, this.output_area),
345 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
346 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
346 },
347 },
347 input : $.proxy(this._handle_input_request, this)
348 input : $.proxy(this._handle_input_request, this)
348 };
349 };
349 };
350 };
350
351
351 CodeCell.prototype._open_with_pager = function (payload) {
352 CodeCell.prototype._open_with_pager = function (payload) {
352 $([IPython.events]).trigger('open_with_text.Pager', payload);
353 $([IPython.events]).trigger('open_with_text.Pager', payload);
353 };
354 };
354
355
355 /**
356 /**
356 * @method _handle_execute_reply
357 * @method _handle_execute_reply
357 * @private
358 * @private
358 */
359 */
359 CodeCell.prototype._handle_execute_reply = function (msg) {
360 CodeCell.prototype._handle_execute_reply = function (msg) {
360 this.set_input_prompt(msg.content.execution_count);
361 this.set_input_prompt(msg.content.execution_count);
361 this.element.removeClass("running");
362 this.element.removeClass("running");
362 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
363 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
363 };
364 };
364
365
365 /**
366 /**
366 * @method _handle_set_next_input
367 * @method _handle_set_next_input
367 * @private
368 * @private
368 */
369 */
369 CodeCell.prototype._handle_set_next_input = function (payload) {
370 CodeCell.prototype._handle_set_next_input = function (payload) {
370 var data = {'cell': this, 'text': payload.text};
371 var data = {'cell': this, 'text': payload.text};
371 $([IPython.events]).trigger('set_next_input.Notebook', data);
372 $([IPython.events]).trigger('set_next_input.Notebook', data);
372 };
373 };
373
374
374 /**
375 /**
375 * @method _handle_input_request
376 * @method _handle_input_request
376 * @private
377 * @private
377 */
378 */
378 CodeCell.prototype._handle_input_request = function (msg) {
379 CodeCell.prototype._handle_input_request = function (msg) {
379 this.output_area.append_raw_input(msg);
380 this.output_area.append_raw_input(msg);
380 };
381 };
381
382
382
383
383 // Basic cell manipulation.
384 // Basic cell manipulation.
384
385
385 CodeCell.prototype.select = function () {
386 CodeCell.prototype.select = function () {
386 var cont = IPython.Cell.prototype.select.apply(this);
387 var cont = IPython.Cell.prototype.select.apply(this);
387 if (cont) {
388 if (cont) {
388 this.code_mirror.refresh();
389 this.code_mirror.refresh();
389 this.auto_highlight();
390 this.auto_highlight();
390 }
391 }
391 return cont;
392 return cont;
392 };
393 };
393
394
394 CodeCell.prototype.render = function () {
395 CodeCell.prototype.render = function () {
395 var cont = IPython.Cell.prototype.render.apply(this);
396 var cont = IPython.Cell.prototype.render.apply(this);
396 // Always execute, even if we are already in the rendered state
397 // Always execute, even if we are already in the rendered state
397 return cont;
398 return cont;
398 };
399 };
399
400
400 CodeCell.prototype.unrender = function () {
401 CodeCell.prototype.unrender = function () {
401 // CodeCell is always rendered
402 // CodeCell is always rendered
402 return false;
403 return false;
403 };
404 };
404
405
405 /**
406 /**
406 * Determine whether or not the unfocus event should be aknowledged.
407 * Determine whether or not the unfocus event should be aknowledged.
407 *
408 *
408 * @method should_cancel_blur
409 * @method should_cancel_blur
409 *
410 *
410 * @return results {bool} Whether or not to ignore the cell's blur event.
411 * @return results {bool} Whether or not to ignore the cell's blur event.
411 **/
412 **/
412 CodeCell.prototype.should_cancel_blur = function () {
413 CodeCell.prototype.should_cancel_blur = function () {
413 // Cancel this unfocus event if the base wants to cancel or the cell
414 // Cancel this unfocus event if the base wants to cancel or the cell
414 // completer is open or the tooltip is open.
415 // completer is open or the tooltip is open.
415 return IPython.Cell.prototype.should_cancel_blur.apply(this) ||
416 return IPython.Cell.prototype.should_cancel_blur.apply(this) ||
416 (this.completer && this.completer.is_visible()) ||
417 (this.completer && this.completer.is_visible()) ||
417 (IPython.tooltip && IPython.tooltip.is_visible());
418 (IPython.tooltip && IPython.tooltip.is_visible());
418 };
419 };
419
420
420 CodeCell.prototype.select_all = function () {
421 CodeCell.prototype.select_all = function () {
421 var start = {line: 0, ch: 0};
422 var start = {line: 0, ch: 0};
422 var nlines = this.code_mirror.lineCount();
423 var nlines = this.code_mirror.lineCount();
423 var last_line = this.code_mirror.getLine(nlines-1);
424 var last_line = this.code_mirror.getLine(nlines-1);
424 var end = {line: nlines-1, ch: last_line.length};
425 var end = {line: nlines-1, ch: last_line.length};
425 this.code_mirror.setSelection(start, end);
426 this.code_mirror.setSelection(start, end);
426 };
427 };
427
428
428
429
429 CodeCell.prototype.collapse_output = function () {
430 CodeCell.prototype.collapse_output = function () {
430 this.collapsed = true;
431 this.collapsed = true;
431 this.output_area.collapse();
432 this.output_area.collapse();
432 };
433 };
433
434
434
435
435 CodeCell.prototype.expand_output = function () {
436 CodeCell.prototype.expand_output = function () {
436 this.collapsed = false;
437 this.collapsed = false;
437 this.output_area.expand();
438 this.output_area.expand();
438 this.output_area.unscroll_area();
439 this.output_area.unscroll_area();
439 };
440 };
440
441
441 CodeCell.prototype.scroll_output = function () {
442 CodeCell.prototype.scroll_output = function () {
442 this.output_area.expand();
443 this.output_area.expand();
443 this.output_area.scroll_if_long();
444 this.output_area.scroll_if_long();
444 };
445 };
445
446
446 CodeCell.prototype.toggle_output = function () {
447 CodeCell.prototype.toggle_output = function () {
447 this.collapsed = Boolean(1 - this.collapsed);
448 this.collapsed = Boolean(1 - this.collapsed);
448 this.output_area.toggle_output();
449 this.output_area.toggle_output();
449 };
450 };
450
451
451 CodeCell.prototype.toggle_output_scroll = function () {
452 CodeCell.prototype.toggle_output_scroll = function () {
452 this.output_area.toggle_scroll();
453 this.output_area.toggle_scroll();
453 };
454 };
454
455
455
456
456 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
457 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
457 var ns;
458 var ns;
458 if (prompt_value == undefined) {
459 if (prompt_value == undefined) {
459 ns = "&nbsp;";
460 ns = "&nbsp;";
460 } else {
461 } else {
461 ns = encodeURIComponent(prompt_value);
462 ns = encodeURIComponent(prompt_value);
462 }
463 }
463 return 'In&nbsp;[' + ns + ']:';
464 return 'In&nbsp;[' + ns + ']:';
464 };
465 };
465
466
466 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
467 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
467 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
468 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
468 for(var i=1; i < lines_number; i++) {
469 for(var i=1; i < lines_number; i++) {
469 html.push(['...:']);
470 html.push(['...:']);
470 }
471 }
471 return html.join('<br/>');
472 return html.join('<br/>');
472 };
473 };
473
474
474 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
475 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
475
476
476
477
477 CodeCell.prototype.set_input_prompt = function (number) {
478 CodeCell.prototype.set_input_prompt = function (number) {
478 var nline = 1;
479 var nline = 1;
479 if (this.code_mirror !== undefined) {
480 if (this.code_mirror !== undefined) {
480 nline = this.code_mirror.lineCount();
481 nline = this.code_mirror.lineCount();
481 }
482 }
482 this.input_prompt_number = number;
483 this.input_prompt_number = number;
483 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
484 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
484 // This HTML call is okay because the user contents are escaped.
485 // This HTML call is okay because the user contents are escaped.
485 this.element.find('div.input_prompt').html(prompt_html);
486 this.element.find('div.input_prompt').html(prompt_html);
486 };
487 };
487
488
488
489
489 CodeCell.prototype.clear_input = function () {
490 CodeCell.prototype.clear_input = function () {
490 this.code_mirror.setValue('');
491 this.code_mirror.setValue('');
491 };
492 };
492
493
493
494
494 CodeCell.prototype.get_text = function () {
495 CodeCell.prototype.get_text = function () {
495 return this.code_mirror.getValue();
496 return this.code_mirror.getValue();
496 };
497 };
497
498
498
499
499 CodeCell.prototype.set_text = function (code) {
500 CodeCell.prototype.set_text = function (code) {
500 return this.code_mirror.setValue(code);
501 return this.code_mirror.setValue(code);
501 };
502 };
502
503
503
504
504 CodeCell.prototype.at_top = function () {
505 CodeCell.prototype.at_top = function () {
505 var cursor = this.code_mirror.getCursor();
506 var cursor = this.code_mirror.getCursor();
506 if (cursor.line === 0 && cursor.ch === 0) {
507 if (cursor.line === 0 && cursor.ch === 0) {
507 return true;
508 return true;
508 } else {
509 } else {
509 return false;
510 return false;
510 }
511 }
511 };
512 };
512
513
513
514
514 CodeCell.prototype.at_bottom = function () {
515 CodeCell.prototype.at_bottom = function () {
515 var cursor = this.code_mirror.getCursor();
516 var cursor = this.code_mirror.getCursor();
516 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
517 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
517 return true;
518 return true;
518 } else {
519 } else {
519 return false;
520 return false;
520 }
521 }
521 };
522 };
522
523
523
524
524 CodeCell.prototype.clear_output = function (wait) {
525 CodeCell.prototype.clear_output = function (wait) {
525 this.output_area.clear_output(wait);
526 this.output_area.clear_output(wait);
526 this.set_input_prompt();
527 this.set_input_prompt();
527 };
528 };
528
529
529
530
530 // JSON serialization
531 // JSON serialization
531
532
532 CodeCell.prototype.fromJSON = function (data) {
533 CodeCell.prototype.fromJSON = function (data) {
533 IPython.Cell.prototype.fromJSON.apply(this, arguments);
534 IPython.Cell.prototype.fromJSON.apply(this, arguments);
534 if (data.cell_type === 'code') {
535 if (data.cell_type === 'code') {
535 if (data.input !== undefined) {
536 if (data.input !== undefined) {
536 this.set_text(data.input);
537 this.set_text(data.input);
537 // make this value the starting point, so that we can only undo
538 // make this value the starting point, so that we can only undo
538 // to this state, instead of a blank cell
539 // to this state, instead of a blank cell
539 this.code_mirror.clearHistory();
540 this.code_mirror.clearHistory();
540 this.auto_highlight();
541 this.auto_highlight();
541 }
542 }
542 if (data.prompt_number !== undefined) {
543 if (data.prompt_number !== undefined) {
543 this.set_input_prompt(data.prompt_number);
544 this.set_input_prompt(data.prompt_number);
544 } else {
545 } else {
545 this.set_input_prompt();
546 this.set_input_prompt();
546 }
547 }
547 this.output_area.trusted = data.trusted || false;
548 this.output_area.trusted = data.trusted || false;
548 this.output_area.fromJSON(data.outputs);
549 this.output_area.fromJSON(data.outputs);
549 if (data.collapsed !== undefined) {
550 if (data.collapsed !== undefined) {
550 if (data.collapsed) {
551 if (data.collapsed) {
551 this.collapse_output();
552 this.collapse_output();
552 } else {
553 } else {
553 this.expand_output();
554 this.expand_output();
554 }
555 }
555 }
556 }
556 }
557 }
557 };
558 };
558
559
559
560
560 CodeCell.prototype.toJSON = function () {
561 CodeCell.prototype.toJSON = function () {
561 var data = IPython.Cell.prototype.toJSON.apply(this);
562 var data = IPython.Cell.prototype.toJSON.apply(this);
562 data.input = this.get_text();
563 data.input = this.get_text();
563 // is finite protect against undefined and '*' value
564 // is finite protect against undefined and '*' value
564 if (isFinite(this.input_prompt_number)) {
565 if (isFinite(this.input_prompt_number)) {
565 data.prompt_number = this.input_prompt_number;
566 data.prompt_number = this.input_prompt_number;
566 }
567 }
567 var outputs = this.output_area.toJSON();
568 var outputs = this.output_area.toJSON();
568 data.outputs = outputs;
569 data.outputs = outputs;
569 data.language = 'python';
570 data.language = 'python';
570 data.trusted = this.output_area.trusted;
571 data.trusted = this.output_area.trusted;
571 data.collapsed = this.collapsed;
572 data.collapsed = this.collapsed;
572 return data;
573 return data;
573 };
574 };
574
575
575
576
576 IPython.CodeCell = CodeCell;
577 IPython.CodeCell = CodeCell;
577
578
578 return IPython;
579 return IPython;
579 }(IPython));
580 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now