##// END OF EJS Templates
some optimisation and code cleaning...
Matthias BUSSONNIER -
Show More
@@ -1,363 +1,370
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 // Cell
9 // Cell
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 Cell
15 * @submodule Cell
16 */
16 */
17
17
18 var IPython = (function (IPython) {
18 var IPython = (function (IPython) {
19 "use strict";
19 "use strict";
20
20
21 var utils = IPython.utils;
21 var utils = IPython.utils;
22
22
23 /**
23 /**
24 * The Base `Cell` class from which to inherit
24 * The Base `Cell` class from which to inherit
25 * @class Cell
25 * @class Cell
26 **/
26 **/
27
27
28 /*
28 /*
29 * @constructor
29 * @constructor
30 *
30 *
31 * @param {object|undefined} [options]
31 * @param {object|undefined} [options]
32 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
32 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
33 */
33 */
34 var Cell = function (options) {
34 var Cell = function (options) {
35
35
36 options = this.mergeopt(Cell, options)
36 options = this.mergeopt(Cell, options)
37 // superclass default overwrite our default
37 // superclass default overwrite our default
38
38
39 this.placeholder = options.placeholder || '';
39 this.placeholder = options.placeholder || '';
40 this.read_only = options.cm_config.readOnly;
40 this.read_only = options.cm_config.readOnly;
41 this.selected = false;
41 this.selected = false;
42 this.element = null;
43 this.metadata = {};
42 this.metadata = {};
44 // load this from metadata later ?
43 // load this from metadata later ?
45 this.user_highlight = 'auto';
44 this.user_highlight = 'auto';
46 this.cm_config = options.cm_config;
45 this.cm_config = options.cm_config;
46 this.cell_id = utils.uuid();
47 this._options = options;
48
49 // For JS VM engines optimisation, attributes should be all set (even
50 // to null) in the constructor, and if possible, if different subclass
51 // have new attributes with same name, they should be created in the
52 // same order. Easiest is to create and set to null in parent class.
53
54 this.element = null;
55 this.cell_type = null;
56 this.code_mirror = null;
57
58
47 this.create_element();
59 this.create_element();
48 if (this.element !== null) {
60 if (this.element !== null) {
49 this.element.data("cell", this);
61 this.element.data("cell", this);
50 this.bind_events();
62 this.bind_events();
51 }
63 }
52 this.cell_id = utils.uuid();
53 this._options = options;
54 };
64 };
55
65
56 Cell.options_default = {
66 Cell.options_default = {
57 cm_config : {
67 cm_config : {
58 indentUnit : 4,
68 indentUnit : 4,
59 readOnly: false,
69 readOnly: false,
60 theme: "default"
70 theme: "default"
61 }
71 }
62 };
72 };
63
73
64 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
74 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
65 // by disabling drag/drop altogether on Safari
75 // by disabling drag/drop altogether on Safari
66 // https://github.com/marijnh/CodeMirror/issues/332
76 // https://github.com/marijnh/CodeMirror/issues/332
67
77
68 if (utils.browser[0] == "Safari") {
78 if (utils.browser[0] == "Safari") {
69 Cell.options_default.cm_config.dragDrop = false;
79 Cell.options_default.cm_config.dragDrop = false;
70 }
80 }
71
81
72 Cell.prototype.mergeopt = function(_class, options, overwrite){
82 Cell.prototype.mergeopt = function(_class, options, overwrite){
73 overwrite = overwrite || {};
83 overwrite = overwrite || {};
74 return $.extend(true, {}, _class.options_default, options, overwrite)
84 return $.extend(true, {}, _class.options_default, options, overwrite)
75
85
76 }
86 }
77
87
78
88
79
89
80 /**
90 /**
81 * Empty. Subclasses must implement create_element.
91 * Empty. Subclasses must implement create_element.
82 * This should contain all the code to create the DOM element in notebook
92 * This should contain all the code to create the DOM element in notebook
83 * and will be called by Base Class constructor.
93 * and will be called by Base Class constructor.
84 * @method create_element
94 * @method create_element
85 */
95 */
86 Cell.prototype.create_element = function () {
96 Cell.prototype.create_element = function () {
87 };
97 };
88
98
89
99
90 /**
100 /**
91 * Subclasses can implement override bind_events.
101 * Subclasses can implement override bind_events.
92 * Be carefull to call the parent method when overwriting as it fires event.
102 * Be carefull to call the parent method when overwriting as it fires event.
93 * this will be triggerd after create_element in constructor.
103 * this will be triggerd after create_element in constructor.
94 * @method bind_events
104 * @method bind_events
95 */
105 */
96 Cell.prototype.bind_events = function () {
106 Cell.prototype.bind_events = function () {
97 var that = this;
107 var that = this;
98 // We trigger events so that Cell doesn't have to depend on Notebook.
108 // We trigger events so that Cell doesn't have to depend on Notebook.
99 that.element.click(function (event) {
109 that.element.click(function (event) {
100 if (that.selected === false) {
110 if (that.selected === false) {
101 $([IPython.events]).trigger('select.Cell', {'cell':that});
111 $([IPython.events]).trigger('select.Cell', {'cell':that});
102 }
112 }
103 });
113 });
104 that.element.focusin(function (event) {
114 that.element.focusin(function (event) {
105 if (that.selected === false) {
115 if (that.selected === false) {
106 $([IPython.events]).trigger('select.Cell', {'cell':that});
116 $([IPython.events]).trigger('select.Cell', {'cell':that});
107 }
117 }
108 });
118 });
109 if (this.code_mirror) {
119 if (this.code_mirror) {
110 this.code_mirror.on("change", function(cm, change) {
120 this.code_mirror.on("change", function(cm, change) {
111 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
121 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
112 });
122 });
113 }
123 }
114 };
124 };
115
125
116 /**
126 /**
117 * Triger typsetting of math by mathjax on current cell element
127 * Triger typsetting of math by mathjax on current cell element
118 * @method typeset
128 * @method typeset
119 */
129 */
120 Cell.prototype.typeset = function () {
130 Cell.prototype.typeset = function () {
121 if (window.MathJax){
131 if (window.MathJax){
122 var cell_math = this.element.get(0);
132 var cell_math = this.element.get(0);
123 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
133 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
124 }
134 }
125 };
135 };
126
136
127 /**
137 /**
128 * should be triggerd when cell is selected
138 * should be triggerd when cell is selected
129 * @method select
139 * @method select
130 */
140 */
131 Cell.prototype.select = function () {
141 Cell.prototype.select = function () {
132 this.element.addClass('selected');
142 this.element.addClass('selected');
133 this.selected = true;
143 this.selected = true;
134 };
144 };
135
145
136
146
137 /**
147 /**
138 * should be triggerd when cell is unselected
148 * should be triggerd when cell is unselected
139 * @method unselect
149 * @method unselect
140 */
150 */
141 Cell.prototype.unselect = function () {
151 Cell.prototype.unselect = function () {
142 this.element.removeClass('selected');
152 this.element.removeClass('selected');
143 this.selected = false;
153 this.selected = false;
144 };
154 };
145
155
146 /**
156 /**
147 * should be overritten by subclass
157 * should be overritten by subclass
148 * @method get_text
158 * @method get_text
149 */
159 */
150 Cell.prototype.get_text = function () {
160 Cell.prototype.get_text = function () {
151 };
161 };
152
162
153 /**
163 /**
154 * should be overritten by subclass
164 * should be overritten by subclass
155 * @method set_text
165 * @method set_text
156 * @param {string} text
166 * @param {string} text
157 */
167 */
158 Cell.prototype.set_text = function (text) {
168 Cell.prototype.set_text = function (text) {
159 };
169 };
160
170
161 /**
171 /**
162 * Refresh codemirror instance
172 * Refresh codemirror instance
163 * @method refresh
173 * @method refresh
164 */
174 */
165 Cell.prototype.refresh = function () {
175 Cell.prototype.refresh = function () {
166 this.code_mirror.refresh();
176 this.code_mirror.refresh();
167 };
177 };
168
178
169
179
170 /**
180 /**
171 * should be overritten by subclass
181 * should be overritten by subclass
172 * @method edit
182 * @method edit
173 **/
183 **/
174 Cell.prototype.edit = function () {
184 Cell.prototype.edit = function () {
175 };
185 };
176
186
177
187
178 /**
188 /**
179 * should be overritten by subclass
189 * should be overritten by subclass
180 * @method render
190 * @method render
181 **/
191 **/
182 Cell.prototype.render = function () {
192 Cell.prototype.render = function () {
183 };
193 };
184
194
185 /**
195 /**
186 * should be overritten by subclass
196 * should be overritten by subclass
187 * serialise cell to json.
197 * serialise cell to json.
188 * @method toJSON
198 * @method toJSON
189 **/
199 **/
190 Cell.prototype.toJSON = function () {
200 Cell.prototype.toJSON = function () {
191 var data = {};
201 var data = {};
192 data.metadata = this.metadata;
202 data.metadata = this.metadata;
193 return data;
203 return data;
194 };
204 };
195
205
196
206
197 /**
207 /**
198 * should be overritten by subclass
208 * should be overritten by subclass
199 * @method fromJSON
209 * @method fromJSON
200 **/
210 **/
201 Cell.prototype.fromJSON = function (data) {
211 Cell.prototype.fromJSON = function (data) {
202 if (data.metadata !== undefined) {
212 if (data.metadata !== undefined) {
203 this.metadata = data.metadata;
213 this.metadata = data.metadata;
204 }
214 }
205 this.celltoolbar.rebuild();
215 this.celltoolbar.rebuild();
206 };
216 };
207
217
208
218
209 /**
219 /**
210 * can the cell be split into two cells
220 * can the cell be split into two cells
211 * @method is_splittable
221 * @method is_splittable
212 **/
222 **/
213 Cell.prototype.is_splittable = function () {
223 Cell.prototype.is_splittable = function () {
214 return true;
224 return true;
215 };
225 };
216
226
217
227
218 /**
228 /**
219 * can the cell be merged with other cells
229 * can the cell be merged with other cells
220 * @method is_mergeable
230 * @method is_mergeable
221 **/
231 **/
222 Cell.prototype.is_mergeable = function () {
232 Cell.prototype.is_mergeable = function () {
223 return true;
233 return true;
224 };
234 };
225
235
226
236
227 /**
237 /**
228 * @return {String} - the text before the cursor
238 * @return {String} - the text before the cursor
229 * @method get_pre_cursor
239 * @method get_pre_cursor
230 **/
240 **/
231 Cell.prototype.get_pre_cursor = function () {
241 Cell.prototype.get_pre_cursor = function () {
232 var cursor = this.code_mirror.getCursor();
242 var cursor = this.code_mirror.getCursor();
233 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
243 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
234 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
244 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
235 return text;
245 return text;
236 }
246 }
237
247
238
248
239 /**
249 /**
240 * @return {String} - the text after the cursor
250 * @return {String} - the text after the cursor
241 * @method get_post_cursor
251 * @method get_post_cursor
242 **/
252 **/
243 Cell.prototype.get_post_cursor = function () {
253 Cell.prototype.get_post_cursor = function () {
244 var cursor = this.code_mirror.getCursor();
254 var cursor = this.code_mirror.getCursor();
245 var last_line_num = this.code_mirror.lineCount()-1;
255 var last_line_num = this.code_mirror.lineCount()-1;
246 var last_line_len = this.code_mirror.getLine(last_line_num).length;
256 var last_line_len = this.code_mirror.getLine(last_line_num).length;
247 var end = {line:last_line_num, ch:last_line_len}
257 var end = {line:last_line_num, ch:last_line_len}
248 var text = this.code_mirror.getRange(cursor, end);
258 var text = this.code_mirror.getRange(cursor, end);
249 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
259 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
250 return text;
260 return text;
251 };
261 };
252
262
253 /**
263 /**
254 * Show/Hide CodeMirror LineNumber
264 * Show/Hide CodeMirror LineNumber
255 * @method show_line_numbers
265 * @method show_line_numbers
256 *
266 *
257 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
267 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
258 **/
268 **/
259 Cell.prototype.show_line_numbers = function (value) {
269 Cell.prototype.show_line_numbers = function (value) {
260 this.code_mirror.setOption('lineNumbers', value);
270 this.code_mirror.setOption('lineNumbers', value);
261 this.code_mirror.refresh();
271 this.code_mirror.refresh();
262 };
272 };
263
273
264 /**
274 /**
265 * Toggle CodeMirror LineNumber
275 * Toggle CodeMirror LineNumber
266 * @method toggle_line_numbers
276 * @method toggle_line_numbers
267 **/
277 **/
268 Cell.prototype.toggle_line_numbers = function () {
278 Cell.prototype.toggle_line_numbers = function () {
269 var val = this.code_mirror.getOption('lineNumbers');
279 var val = this.code_mirror.getOption('lineNumbers');
270 this.show_line_numbers(!val);
280 this.show_line_numbers(!val);
271 };
281 };
272
282
273 /**
283 /**
274 * Force codemirror highlight mode
284 * Force codemirror highlight mode
275 * @method force_highlight
285 * @method force_highlight
276 * @param {object} - CodeMirror mode
286 * @param {object} - CodeMirror mode
277 **/
287 **/
278 Cell.prototype.force_highlight = function(mode) {
288 Cell.prototype.force_highlight = function(mode) {
279 this.user_highlight = mode;
289 this.user_highlight = mode;
280 this.auto_highlight();
290 this.auto_highlight();
281 };
291 };
282
292
283 /**
293 /**
284 * Try to autodetect cell highlight mode, or use selected mode
294 * Try to autodetect cell highlight mode, or use selected mode
285 * @methods _auto_highlight
295 * @methods _auto_highlight
286 * @private
296 * @private
287 * @param {String|object|undefined} - CodeMirror mode | 'auto'
297 * @param {String|object|undefined} - CodeMirror mode | 'auto'
288 **/
298 **/
289 Cell.prototype._auto_highlight = function (modes) {
299 Cell.prototype._auto_highlight = function (modes) {
290 //Here we handle manually selected modes
300 //Here we handle manually selected modes
291 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
301 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
292 {
302 {
293 var mode = this.user_highlight;
303 var mode = this.user_highlight;
294 CodeMirror.autoLoadMode(this.code_mirror, mode);
304 CodeMirror.autoLoadMode(this.code_mirror, mode);
295 this.code_mirror.setOption('mode', mode);
305 this.code_mirror.setOption('mode', mode);
296 return;
306 return;
297 }
307 }
298 var current_mode = this.code_mirror.getOption('mode', mode);
308 var current_mode = this.code_mirror.getOption('mode', mode);
299 var first_line = this.code_mirror.getLine(0);
309 var first_line = this.code_mirror.getLine(0);
300 // loop on every pairs
310 // loop on every pairs
301 for( var mode in modes) {
311 for( var mode in modes) {
302 var regs = modes[mode]['reg'];
312 var regs = modes[mode]['reg'];
303 // only one key every time but regexp can't be keys...
313 // only one key every time but regexp can't be keys...
304 for(var reg in regs ) {
314 for(var reg in regs ) {
305 // here we handle non magic_modes
315 // here we handle non magic_modes
306 if(first_line.match(regs[reg]) != null) {
316 if(first_line.match(regs[reg]) != null) {
307 if(current_mode == mode){
317 if(current_mode == mode){
308 return;
318 return;
309 }
319 }
310 if (mode.search('magic_') != 0) {
320 if (mode.search('magic_') != 0) {
311 this.code_mirror.setOption('mode', mode);
321 this.code_mirror.setOption('mode', mode);
312 console.log('from',current_mode,'to',mode)
313 CodeMirror.autoLoadMode(this.code_mirror, mode);
322 CodeMirror.autoLoadMode(this.code_mirror, mode);
314 return;
323 return;
315 }
324 }
316 var open = modes[mode]['open']|| "%%";
325 var open = modes[mode]['open']|| "%%";
317 var close = modes[mode]['close']|| "%%end";
326 var close = modes[mode]['close']|| "%%end";
318 var mmode = mode;
327 var mmode = mode;
319 mode = mmode.substr(6);
328 mode = mmode.substr(6);
320 if(current_mode == mode){
329 if(current_mode == mode){
321 return;
330 return;
322 }
331 }
323 CodeMirror.autoLoadMode(this.code_mirror, mode);
332 CodeMirror.autoLoadMode(this.code_mirror, mode);
324 // create on the fly a mode that swhitch between
333 // create on the fly a mode that swhitch between
325 // plain/text and smth else otherwise `%%` is
334 // plain/text and smth else otherwise `%%` is
326 // source of some highlight issues.
335 // source of some highlight issues.
327 // we use patchedGetMode to circumvent a bug in CM
336 // we use patchedGetMode to circumvent a bug in CM
328 CodeMirror.defineMode(mmode , function(config) {
337 CodeMirror.defineMode(mmode , function(config) {
329 return CodeMirror.multiplexingMode(
338 return CodeMirror.multiplexingMode(
330 CodeMirror.patchedGetMode(config, 'text/plain'),
339 CodeMirror.patchedGetMode(config, 'text/plain'),
331 // always set someting on close
340 // always set someting on close
332 {open: open, close: close,
341 {open: open, close: close,
333 mode: CodeMirror.patchedGetMode(config, mode),
342 mode: CodeMirror.patchedGetMode(config, mode),
334 delimStyle: "delimit"
343 delimStyle: "delimit"
335 }
344 }
336 );
345 );
337 });
346 });
338 this.code_mirror.setOption('mode', mmode);
347 this.code_mirror.setOption('mode', mmode);
339 console.log('from',current_mode,'to', mmode)
340 return;
348 return;
341 }
349 }
342 }
350 }
343 }
351 }
344 // fallback on default
352 // fallback on default
345 var default_mode
353 var default_mode
346 try {
354 try {
347 default_mode = this._options.cm_config.mode;
355 default_mode = this._options.cm_config.mode;
348 } catch(e) {
356 } catch(e) {
349 default_mode = 'text/plain';
357 default_mode = 'text/plain';
350 }
358 }
351 if( current_mode === default_mode){
359 if( current_mode === default_mode){
352 return
360 return
353 }
361 }
354 this.code_mirror.setOption('mode', default_mode);
362 this.code_mirror.setOption('mode', default_mode);
355 console.log('from',current_mode,'to', default_mode)
356 };
363 };
357
364
358 IPython.Cell = Cell;
365 IPython.Cell = Cell;
359
366
360 return IPython;
367 return IPython;
361
368
362 }(IPython));
369 }(IPython));
363
370
@@ -1,463 +1,463
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 key = IPython.utils.keycodes;
48 var key = IPython.utils.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.code_mirror = null;
66 this.input_prompt_number = null;
67 this.collapsed = false;
65 this.collapsed = false;
68 this.cell_type = "code";
66 this.cell_type = "code";
69
67
68 // create all attributed in constructor function
69 // even if null for V8 VM optimisation
70 this.input_prompt_number = null;
71 this.celltoolbar = null;
72 this.output_area = null;
73 this.last_msg_id = null;
74 this.completer = null;
75
70
76
71 var cm_overwrite_options = {
77 var cm_overwrite_options = {
72 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
78 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
73 };
79 };
74
80
75 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
81 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
76
82
77 IPython.Cell.apply(this,[options]);
83 IPython.Cell.apply(this,[options]);
78
84
79 var that = this;
85 var that = this;
80 this.element.focusout(
86 this.element.focusout(
81 function() { that.auto_highlight(); }
87 function() { that.auto_highlight(); }
82 );
88 );
83 };
89 };
84
90
85 CodeCell.options_default = {
91 CodeCell.options_default = {
86 cm_config : {
92 cm_config : {
87 extraKeys: {
93 extraKeys: {
88 "Tab" : "indentMore",
94 "Tab" : "indentMore",
89 "Shift-Tab" : "indentLess",
95 "Shift-Tab" : "indentLess",
90 "Backspace" : "delSpaceToPrevTabStop",
96 "Backspace" : "delSpaceToPrevTabStop",
91 "Cmd-/" : "toggleComment",
97 "Cmd-/" : "toggleComment",
92 "Ctrl-/" : "toggleComment"
98 "Ctrl-/" : "toggleComment"
93 },
99 },
94 mode: 'ipython',
100 mode: 'ipython',
95 theme: 'ipython',
101 theme: 'ipython',
96 matchBrackets: true
102 matchBrackets: true
97 }
103 }
98 };
104 };
99
105
100
106
101 CodeCell.prototype = new IPython.Cell();
107 CodeCell.prototype = new IPython.Cell();
102
108
103 /**
109 /**
104 * @method auto_highlight
110 * @method auto_highlight
105 */
111 */
106 CodeCell.prototype.auto_highlight = function () {
112 CodeCell.prototype.auto_highlight = function () {
107 this._auto_highlight(IPython.config.cell_magic_highlight);
113 this._auto_highlight(IPython.config.cell_magic_highlight);
108 };
114 };
109
115
110 /** @method create_element */
116 /** @method create_element */
111 CodeCell.prototype.create_element = function () {
117 CodeCell.prototype.create_element = function () {
112 IPython.Cell.prototype.create_element.apply(this, arguments);
118 IPython.Cell.prototype.create_element.apply(this, arguments);
113
119
114 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
120 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
115 cell.attr('tabindex','2');
121 cell.attr('tabindex','2');
116
122
117 this.celltoolbar = new IPython.CellToolbar(this);
123 this.celltoolbar = new IPython.CellToolbar(this);
118
124
119 var input = $('<div></div>').addClass('input');
125 var input = $('<div></div>').addClass('input');
120 var vbox = $('<div/>').addClass('vbox box-flex1');
126 var vbox = $('<div/>').addClass('vbox box-flex1');
121 input.append($('<div/>').addClass('prompt input_prompt'));
127 input.append($('<div/>').addClass('prompt input_prompt'));
122 vbox.append(this.celltoolbar.element);
128 vbox.append(this.celltoolbar.element);
123 var input_area = $('<div/>').addClass('input_area');
129 var input_area = $('<div/>').addClass('input_area');
124 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
130 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
125 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
131 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
126 vbox.append(input_area);
132 vbox.append(input_area);
127 input.append(vbox);
133 input.append(vbox);
128 var output = $('<div></div>');
134 var output = $('<div></div>');
129 cell.append(input).append(output);
135 cell.append(input).append(output);
130 this.element = cell;
136 this.element = cell;
131 this.output_area = new IPython.OutputArea(output, true);
137 this.output_area = new IPython.OutputArea(output, true);
132
138 this.completer = new IPython.Completer(this);
133 // construct a completer only if class exist
134 // otherwise no print view
135 if (IPython.Completer !== undefined)
136 {
137 this.completer = new IPython.Completer(this);
138 }
139 };
139 };
140
140
141 /**
141 /**
142 * This method gets called in CodeMirror's onKeyDown/onKeyPress
142 * This method gets called in CodeMirror's onKeyDown/onKeyPress
143 * handlers and is used to provide custom key handling. Its return
143 * handlers and is used to provide custom key handling. Its return
144 * value is used to determine if CodeMirror should ignore the event:
144 * value is used to determine if CodeMirror should ignore the event:
145 * true = ignore, false = don't ignore.
145 * true = ignore, false = don't ignore.
146 * @method handle_codemirror_keyevent
146 * @method handle_codemirror_keyevent
147 */
147 */
148 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
148 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
149
149
150 var that = this;
150 var that = this;
151 // whatever key is pressed, first, cancel the tooltip request before
151 // whatever key is pressed, first, cancel the tooltip request before
152 // they are sent, and remove tooltip if any, except for tab again
152 // they are sent, and remove tooltip if any, except for tab again
153 if (event.type === 'keydown' && event.which != key.TAB ) {
153 if (event.type === 'keydown' && event.which != key.TAB ) {
154 IPython.tooltip.remove_and_cancel_tooltip();
154 IPython.tooltip.remove_and_cancel_tooltip();
155 }
155 }
156
156
157 var cur = editor.getCursor();
157 var cur = editor.getCursor();
158 if (event.keyCode === key.ENTER){
158 if (event.keyCode === key.ENTER){
159 this.auto_highlight();
159 this.auto_highlight();
160 }
160 }
161
161
162 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
162 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
163 // Always ignore shift-enter in CodeMirror as we handle it.
163 // Always ignore shift-enter in CodeMirror as we handle it.
164 return true;
164 return true;
165 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
165 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
166 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
166 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
167 // browser and keyboard layout !
167 // browser and keyboard layout !
168 // Pressing '(' , request tooltip, don't forget to reappend it
168 // Pressing '(' , request tooltip, don't forget to reappend it
169 // The second argument says to hide the tooltip if the docstring
169 // The second argument says to hide the tooltip if the docstring
170 // is actually empty
170 // is actually empty
171 IPython.tooltip.pending(that, true);
171 IPython.tooltip.pending(that, true);
172 } else if (event.which === key.UPARROW && event.type === 'keydown') {
172 } else if (event.which === key.UPARROW && event.type === 'keydown') {
173 // If we are not at the top, let CM handle the up arrow and
173 // If we are not at the top, let CM handle the up arrow and
174 // prevent the global keydown handler from handling it.
174 // prevent the global keydown handler from handling it.
175 if (!that.at_top()) {
175 if (!that.at_top()) {
176 event.stop();
176 event.stop();
177 return false;
177 return false;
178 } else {
178 } else {
179 return true;
179 return true;
180 }
180 }
181 } else if (event.which === key.ESC) {
181 } else if (event.which === key.ESC) {
182 return IPython.tooltip.remove_and_cancel_tooltip(true);
182 return IPython.tooltip.remove_and_cancel_tooltip(true);
183 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
183 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
184 // If we are not at the bottom, let CM handle the down arrow and
184 // If we are not at the bottom, let CM handle the down arrow and
185 // prevent the global keydown handler from handling it.
185 // prevent the global keydown handler from handling it.
186 if (!that.at_bottom()) {
186 if (!that.at_bottom()) {
187 event.stop();
187 event.stop();
188 return false;
188 return false;
189 } else {
189 } else {
190 return true;
190 return true;
191 }
191 }
192 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
192 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
193 if (editor.somethingSelected()){
193 if (editor.somethingSelected()){
194 var anchor = editor.getCursor("anchor");
194 var anchor = editor.getCursor("anchor");
195 var head = editor.getCursor("head");
195 var head = editor.getCursor("head");
196 if( anchor.line != head.line){
196 if( anchor.line != head.line){
197 return false;
197 return false;
198 }
198 }
199 }
199 }
200 IPython.tooltip.request(that);
200 IPython.tooltip.request(that);
201 event.stop();
201 event.stop();
202 return true;
202 return true;
203 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
203 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
204 // Tab completion.
204 // Tab completion.
205 IPython.tooltip.remove_and_cancel_tooltip();
205 IPython.tooltip.remove_and_cancel_tooltip();
206 if (editor.somethingSelected()) {
206 if (editor.somethingSelected()) {
207 return false;
207 return false;
208 }
208 }
209 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
209 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
210 if (pre_cursor.trim() === "") {
210 if (pre_cursor.trim() === "") {
211 // Don't autocomplete if the part of the line before the cursor
211 // Don't autocomplete if the part of the line before the cursor
212 // is empty. In this case, let CodeMirror handle indentation.
212 // is empty. In this case, let CodeMirror handle indentation.
213 return false;
213 return false;
214 } else {
214 } else {
215 event.stop();
215 event.stop();
216 this.completer.startCompletion();
216 this.completer.startCompletion();
217 return true;
217 return true;
218 }
218 }
219 } else {
219 } else {
220 // keypress/keyup also trigger on TAB press, and we don't want to
220 // keypress/keyup also trigger on TAB press, and we don't want to
221 // use those to disable tab completion.
221 // use those to disable tab completion.
222 return false;
222 return false;
223 }
223 }
224 return false;
224 return false;
225 };
225 };
226
226
227
227
228 // Kernel related calls.
228 // Kernel related calls.
229
229
230 CodeCell.prototype.set_kernel = function (kernel) {
230 CodeCell.prototype.set_kernel = function (kernel) {
231 this.kernel = kernel;
231 this.kernel = kernel;
232 };
232 };
233
233
234 /**
234 /**
235 * Execute current code cell to the kernel
235 * Execute current code cell to the kernel
236 * @method execute
236 * @method execute
237 */
237 */
238 CodeCell.prototype.execute = function () {
238 CodeCell.prototype.execute = function () {
239 this.output_area.clear_output();
239 this.output_area.clear_output();
240 this.set_input_prompt('*');
240 this.set_input_prompt('*');
241 this.element.addClass("running");
241 this.element.addClass("running");
242 if (this.last_msg_id) {
242 if (this.last_msg_id) {
243 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
243 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
244 }
244 }
245 var callbacks = this.get_callbacks();
245 var callbacks = this.get_callbacks();
246
246
247 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
247 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
248 };
248 };
249
249
250 /**
250 /**
251 * Construct the default callbacks for
251 * Construct the default callbacks for
252 * @method get_callbacks
252 * @method get_callbacks
253 */
253 */
254 CodeCell.prototype.get_callbacks = function () {
254 CodeCell.prototype.get_callbacks = function () {
255 return {
255 return {
256 shell : {
256 shell : {
257 reply : $.proxy(this._handle_execute_reply, this),
257 reply : $.proxy(this._handle_execute_reply, this),
258 payload : {
258 payload : {
259 set_next_input : $.proxy(this._handle_set_next_input, this),
259 set_next_input : $.proxy(this._handle_set_next_input, this),
260 page : $.proxy(this._open_with_pager, this)
260 page : $.proxy(this._open_with_pager, this)
261 }
261 }
262 },
262 },
263 iopub : {
263 iopub : {
264 output : $.proxy(this.output_area.handle_output, this.output_area),
264 output : $.proxy(this.output_area.handle_output, this.output_area),
265 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
265 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
266 },
266 },
267 input : $.proxy(this._handle_input_request, this)
267 input : $.proxy(this._handle_input_request, this)
268 };
268 };
269 };
269 };
270
270
271 CodeCell.prototype._open_with_pager = function (payload) {
271 CodeCell.prototype._open_with_pager = function (payload) {
272 $([IPython.events]).trigger('open_with_text.Pager', payload);
272 $([IPython.events]).trigger('open_with_text.Pager', payload);
273 };
273 };
274
274
275 /**
275 /**
276 * @method _handle_execute_reply
276 * @method _handle_execute_reply
277 * @private
277 * @private
278 */
278 */
279 CodeCell.prototype._handle_execute_reply = function (msg) {
279 CodeCell.prototype._handle_execute_reply = function (msg) {
280 this.set_input_prompt(msg.content.execution_count);
280 this.set_input_prompt(msg.content.execution_count);
281 this.element.removeClass("running");
281 this.element.removeClass("running");
282 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
282 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
283 };
283 };
284
284
285 /**
285 /**
286 * @method _handle_set_next_input
286 * @method _handle_set_next_input
287 * @private
287 * @private
288 */
288 */
289 CodeCell.prototype._handle_set_next_input = function (payload) {
289 CodeCell.prototype._handle_set_next_input = function (payload) {
290 var data = {'cell': this, 'text': payload.text};
290 var data = {'cell': this, 'text': payload.text};
291 $([IPython.events]).trigger('set_next_input.Notebook', data);
291 $([IPython.events]).trigger('set_next_input.Notebook', data);
292 };
292 };
293
293
294 /**
294 /**
295 * @method _handle_input_request
295 * @method _handle_input_request
296 * @private
296 * @private
297 */
297 */
298 CodeCell.prototype._handle_input_request = function (msg) {
298 CodeCell.prototype._handle_input_request = function (msg) {
299 this.output_area.append_raw_input(msg);
299 this.output_area.append_raw_input(msg);
300 };
300 };
301
301
302
302
303 // Basic cell manipulation.
303 // Basic cell manipulation.
304
304
305 CodeCell.prototype.select = function () {
305 CodeCell.prototype.select = function () {
306 IPython.Cell.prototype.select.apply(this);
306 IPython.Cell.prototype.select.apply(this);
307 this.code_mirror.refresh();
307 this.code_mirror.refresh();
308 this.code_mirror.focus();
308 this.code_mirror.focus();
309 this.auto_highlight();
309 this.auto_highlight();
310 // We used to need an additional refresh() after the focus, but
310 // We used to need an additional refresh() after the focus, but
311 // it appears that this has been fixed in CM. This bug would show
311 // it appears that this has been fixed in CM. This bug would show
312 // up on FF when a newly loaded markdown cell was edited.
312 // up on FF when a newly loaded markdown cell was edited.
313 };
313 };
314
314
315
315
316 CodeCell.prototype.select_all = function () {
316 CodeCell.prototype.select_all = function () {
317 var start = {line: 0, ch: 0};
317 var start = {line: 0, ch: 0};
318 var nlines = this.code_mirror.lineCount();
318 var nlines = this.code_mirror.lineCount();
319 var last_line = this.code_mirror.getLine(nlines-1);
319 var last_line = this.code_mirror.getLine(nlines-1);
320 var end = {line: nlines-1, ch: last_line.length};
320 var end = {line: nlines-1, ch: last_line.length};
321 this.code_mirror.setSelection(start, end);
321 this.code_mirror.setSelection(start, end);
322 };
322 };
323
323
324
324
325 CodeCell.prototype.collapse = function () {
325 CodeCell.prototype.collapse = function () {
326 this.collapsed = true;
326 this.collapsed = true;
327 this.output_area.collapse();
327 this.output_area.collapse();
328 };
328 };
329
329
330
330
331 CodeCell.prototype.expand = function () {
331 CodeCell.prototype.expand = function () {
332 this.collapsed = false;
332 this.collapsed = false;
333 this.output_area.expand();
333 this.output_area.expand();
334 };
334 };
335
335
336
336
337 CodeCell.prototype.toggle_output = function () {
337 CodeCell.prototype.toggle_output = function () {
338 this.collapsed = Boolean(1 - this.collapsed);
338 this.collapsed = Boolean(1 - this.collapsed);
339 this.output_area.toggle_output();
339 this.output_area.toggle_output();
340 };
340 };
341
341
342
342
343 CodeCell.prototype.toggle_output_scroll = function () {
343 CodeCell.prototype.toggle_output_scroll = function () {
344 this.output_area.toggle_scroll();
344 this.output_area.toggle_scroll();
345 };
345 };
346
346
347
347
348 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
348 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
349 var ns = prompt_value || "&nbsp;";
349 var ns = prompt_value || "&nbsp;";
350 return 'In&nbsp;[' + ns + ']:';
350 return 'In&nbsp;[' + ns + ']:';
351 };
351 };
352
352
353 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
353 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
354 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
354 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
355 for(var i=1; i < lines_number; i++) {
355 for(var i=1; i < lines_number; i++) {
356 html.push(['...:']);
356 html.push(['...:']);
357 }
357 }
358 return html.join('<br/>');
358 return html.join('<br/>');
359 };
359 };
360
360
361 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
361 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
362
362
363
363
364 CodeCell.prototype.set_input_prompt = function (number) {
364 CodeCell.prototype.set_input_prompt = function (number) {
365 var nline = 1;
365 var nline = 1;
366 if (this.code_mirror !== undefined) {
366 if (this.code_mirror !== undefined) {
367 nline = this.code_mirror.lineCount();
367 nline = this.code_mirror.lineCount();
368 }
368 }
369 this.input_prompt_number = number;
369 this.input_prompt_number = number;
370 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
370 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
371 this.element.find('div.input_prompt').html(prompt_html);
371 this.element.find('div.input_prompt').html(prompt_html);
372 };
372 };
373
373
374
374
375 CodeCell.prototype.clear_input = function () {
375 CodeCell.prototype.clear_input = function () {
376 this.code_mirror.setValue('');
376 this.code_mirror.setValue('');
377 };
377 };
378
378
379
379
380 CodeCell.prototype.get_text = function () {
380 CodeCell.prototype.get_text = function () {
381 return this.code_mirror.getValue();
381 return this.code_mirror.getValue();
382 };
382 };
383
383
384
384
385 CodeCell.prototype.set_text = function (code) {
385 CodeCell.prototype.set_text = function (code) {
386 return this.code_mirror.setValue(code);
386 return this.code_mirror.setValue(code);
387 };
387 };
388
388
389
389
390 CodeCell.prototype.at_top = function () {
390 CodeCell.prototype.at_top = function () {
391 var cursor = this.code_mirror.getCursor();
391 var cursor = this.code_mirror.getCursor();
392 if (cursor.line === 0 && cursor.ch === 0) {
392 if (cursor.line === 0 && cursor.ch === 0) {
393 return true;
393 return true;
394 } else {
394 } else {
395 return false;
395 return false;
396 }
396 }
397 };
397 };
398
398
399
399
400 CodeCell.prototype.at_bottom = function () {
400 CodeCell.prototype.at_bottom = function () {
401 var cursor = this.code_mirror.getCursor();
401 var cursor = this.code_mirror.getCursor();
402 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
402 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
403 return true;
403 return true;
404 } else {
404 } else {
405 return false;
405 return false;
406 }
406 }
407 };
407 };
408
408
409
409
410 CodeCell.prototype.clear_output = function (wait) {
410 CodeCell.prototype.clear_output = function (wait) {
411 this.output_area.clear_output(wait);
411 this.output_area.clear_output(wait);
412 };
412 };
413
413
414
414
415 // JSON serialization
415 // JSON serialization
416
416
417 CodeCell.prototype.fromJSON = function (data) {
417 CodeCell.prototype.fromJSON = function (data) {
418 IPython.Cell.prototype.fromJSON.apply(this, arguments);
418 IPython.Cell.prototype.fromJSON.apply(this, arguments);
419 if (data.cell_type === 'code') {
419 if (data.cell_type === 'code') {
420 if (data.input !== undefined) {
420 if (data.input !== undefined) {
421 this.set_text(data.input);
421 this.set_text(data.input);
422 // make this value the starting point, so that we can only undo
422 // make this value the starting point, so that we can only undo
423 // to this state, instead of a blank cell
423 // to this state, instead of a blank cell
424 this.code_mirror.clearHistory();
424 this.code_mirror.clearHistory();
425 this.auto_highlight();
425 this.auto_highlight();
426 }
426 }
427 if (data.prompt_number !== undefined) {
427 if (data.prompt_number !== undefined) {
428 this.set_input_prompt(data.prompt_number);
428 this.set_input_prompt(data.prompt_number);
429 } else {
429 } else {
430 this.set_input_prompt();
430 this.set_input_prompt();
431 }
431 }
432 this.output_area.fromJSON(data.outputs);
432 this.output_area.fromJSON(data.outputs);
433 if (data.collapsed !== undefined) {
433 if (data.collapsed !== undefined) {
434 if (data.collapsed) {
434 if (data.collapsed) {
435 this.collapse();
435 this.collapse();
436 } else {
436 } else {
437 this.expand();
437 this.expand();
438 }
438 }
439 }
439 }
440 }
440 }
441 };
441 };
442
442
443
443
444 CodeCell.prototype.toJSON = function () {
444 CodeCell.prototype.toJSON = function () {
445 var data = IPython.Cell.prototype.toJSON.apply(this);
445 var data = IPython.Cell.prototype.toJSON.apply(this);
446 data.input = this.get_text();
446 data.input = this.get_text();
447 data.cell_type = 'code';
447 data.cell_type = 'code';
448 // is finite protect against undefined and '*' value
448 // is finite protect against undefined and '*' value
449 if (isFinite(this.input_prompt_number)) {
449 if (isFinite(this.input_prompt_number)) {
450 data.prompt_number = this.input_prompt_number;
450 data.prompt_number = this.input_prompt_number;
451 }
451 }
452 var outputs = this.output_area.toJSON();
452 var outputs = this.output_area.toJSON();
453 data.outputs = outputs;
453 data.outputs = outputs;
454 data.language = 'python';
454 data.language = 'python';
455 data.collapsed = this.collapsed;
455 data.collapsed = this.collapsed;
456 return data;
456 return data;
457 };
457 };
458
458
459
459
460 IPython.CodeCell = CodeCell;
460 IPython.CodeCell = CodeCell;
461
461
462 return IPython;
462 return IPython;
463 }(IPython));
463 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now