##// END OF EJS Templates
Fixing styling issues with CellToolbar....
Brian E. Granger -
Show More
@@ -1,53 +1,53 b''
1 /*Css for the metadata edit area*/
1 /* Css for the metadata edit area */
2
2
3 .celltoolbar{
3
4 border:thin solid #DDD;
4 .celltoolbar {
5 margin-left:81px;
5 border: thin solid #DDD;
6 border-bottom:none;
6 margin-left: 0px;
7 border-bottom: none;
7 background : #EEE;
8 background : #EEE;
8 border-top-right-radius: 3px;
9 border-top-right-radius: 3px;
9 border-top-left-radius: 3px;
10 border-top-left-radius: 3px;
10 display:none;
11 }
12
13 .code_cell .celltoolbar{
14 margin-left:81px;
15 }
16
17 .text_cell .celltoolbar{
18 margin-left:0px;
19 }
11 }
20
12
21 .celltoolbar-on div.input_area , .celltoolbar-on div.text_cell_input{
13 .no_input_radius {
22 border-top-right-radius: 0px;
14 border-top-right-radius: 0px;
23 border-top-left-radius: 0px;
15 border-top-left-radius: 0px;
24 }
16 }
25
17
26 .celltoolbar-on .celltoolbar {
18 .text_cell .ctb_prompt {
27 display:block;
19 display: none;
28 }
20 }
29
21
30 .celltoolbar ui-button {
22 .code_cell .ctb_prompt {
31 border :none;
23 display: block;
32 }
24 }
33
25
34 .button_container {
26 .button_container {
35 float:right;
27 float: right;
36 }
28 }
37
29
38 .button_container .ui-state-default, .button_container .ui-state-hover, .button_container .ui-state-hover span{
30 .button_container .ui-state-default, .button_container .ui-state-hover, .button_container .ui-state-hover span{
39 border-radius : 0 0 0 0;
31 border-radius : 0 0 0 0;
40 border : none;
32 border : none;
41 }
33 }
42
34
43 .celltoolbar select {
35 .celltoolbar .button_container select {
44 margin:10px;
36 margin: 10px;
45 margin-top:0px;
37 margin-top: 0px;
46 margin-bottom:0px;
38 margin-bottom: 0px;
39 font-size: 77%;
40 }
41
42 .celltoolbar label span {
43 font-size: 77%;
47 }
44 }
48
45
49 .celltoolbar input[type=checkbox] {
46 .celltoolbar input[type=checkbox] {
50 margin-bottom:1px;
47 margin-bottom: 1px;
48 }
51
49
50 .celltoolbar ui-button {
51 border: none;
52 }
52 }
53
53
@@ -1,315 +1,314 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 // 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
19
20 var utils = IPython.utils;
20 var utils = IPython.utils;
21
21
22 /**
22 /**
23 * The Base `Cell` class from which to inherit
23 * The Base `Cell` class from which to inherit
24 * @class Cell
24 * @class Cell
25 */
25 */
26
26
27 /*
27 /*
28 * @constructor
28 * @constructor
29 */
29 */
30 var Cell = function () {
30 var Cell = function () {
31 this.placeholder = this.placeholder || '';
31 this.placeholder = this.placeholder || '';
32 this.read_only = false;
32 this.read_only = false;
33 this.selected = false;
33 this.selected = false;
34 this.element = null;
34 this.element = null;
35 this.metadata = {};
35 this.metadata = {};
36 // load this from metadata later ?
36 // load this from metadata later ?
37 this.user_highlight = 'auto';
37 this.user_highlight = 'auto';
38 this.create_element();
38 this.create_element();
39 if (this.element !== null) {
39 if (this.element !== null) {
40 this.element.data("cell", this);
40 this.element.data("cell", this);
41 this.bind_events();
41 this.bind_events();
42 }
42 }
43 this.cell_id = utils.uuid();
43 this.cell_id = utils.uuid();
44 };
44 };
45
45
46
46
47 /**
47 /**
48 * Empty. Subclasses must implement create_element.
48 * Empty. Subclasses must implement create_element.
49 * This should contain all the code to create the DOM element in notebook
49 * This should contain all the code to create the DOM element in notebook
50 * and will be called by Base Class constructor.
50 * and will be called by Base Class constructor.
51 * @method create_element
51 * @method create_element
52 */
52 */
53 Cell.prototype.create_element = function () {
53 Cell.prototype.create_element = function () {
54 this.celltoolbar = new IPython.CellToolbar(this);
55 };
54 };
56
55
57
56
58 /**
57 /**
59 * Subclasses can implement override bind_events.
58 * Subclasses can implement override bind_events.
60 * Be carefull to call the parent method when overwriting as it fires event.
59 * Be carefull to call the parent method when overwriting as it fires event.
61 * this will be triggerd after create_element in constructor.
60 * this will be triggerd after create_element in constructor.
62 * @method bind_events
61 * @method bind_events
63 */
62 */
64 Cell.prototype.bind_events = function () {
63 Cell.prototype.bind_events = function () {
65 var that = this;
64 var that = this;
66 // We trigger events so that Cell doesn't have to depend on Notebook.
65 // We trigger events so that Cell doesn't have to depend on Notebook.
67 that.element.click(function (event) {
66 that.element.click(function (event) {
68 if (that.selected === false) {
67 if (that.selected === false) {
69 $([IPython.events]).trigger('select.Cell', {'cell':that});
68 $([IPython.events]).trigger('select.Cell', {'cell':that});
70 }
69 }
71 });
70 });
72 that.element.focusin(function (event) {
71 that.element.focusin(function (event) {
73 if (that.selected === false) {
72 if (that.selected === false) {
74 $([IPython.events]).trigger('select.Cell', {'cell':that});
73 $([IPython.events]).trigger('select.Cell', {'cell':that});
75 }
74 }
76 });
75 });
77 };
76 };
78
77
79 /**
78 /**
80 * Triger typsetting of math by mathjax on current cell element
79 * Triger typsetting of math by mathjax on current cell element
81 * @method typeset
80 * @method typeset
82 */
81 */
83 Cell.prototype.typeset = function () {
82 Cell.prototype.typeset = function () {
84 if (window.MathJax){
83 if (window.MathJax){
85 var cell_math = this.element.get(0);
84 var cell_math = this.element.get(0);
86 MathJax.Hub.Queue(["Typeset",MathJax.Hub,cell_math]);
85 MathJax.Hub.Queue(["Typeset",MathJax.Hub,cell_math]);
87 }
86 }
88 };
87 };
89
88
90 /**
89 /**
91 * should be triggerd when cell is selected
90 * should be triggerd when cell is selected
92 * @method select
91 * @method select
93 */
92 */
94 Cell.prototype.select = function () {
93 Cell.prototype.select = function () {
95 this.element.addClass('ui-widget-content ui-corner-all');
94 this.element.addClass('ui-widget-content ui-corner-all');
96 this.selected = true;
95 this.selected = true;
97 };
96 };
98
97
99
98
100 /**
99 /**
101 * should be triggerd when cell is unselected
100 * should be triggerd when cell is unselected
102 * @method unselect
101 * @method unselect
103 */
102 */
104 Cell.prototype.unselect = function () {
103 Cell.prototype.unselect = function () {
105 this.element.removeClass('ui-widget-content ui-corner-all');
104 this.element.removeClass('ui-widget-content ui-corner-all');
106 this.selected = false;
105 this.selected = false;
107 };
106 };
108
107
109 /**
108 /**
110 * should be overritten by subclass
109 * should be overritten by subclass
111 * @method get_text
110 * @method get_text
112 */
111 */
113 Cell.prototype.get_text = function () {
112 Cell.prototype.get_text = function () {
114 };
113 };
115
114
116 /**
115 /**
117 * should be overritten by subclass
116 * should be overritten by subclass
118 * @method set_text
117 * @method set_text
119 * @param {string} text
118 * @param {string} text
120 */
119 */
121 Cell.prototype.set_text = function (text) {
120 Cell.prototype.set_text = function (text) {
122 };
121 };
123
122
124 /**
123 /**
125 * Refresh codemirror instance
124 * Refresh codemirror instance
126 * @method refresh
125 * @method refresh
127 */
126 */
128 Cell.prototype.refresh = function () {
127 Cell.prototype.refresh = function () {
129 this.code_mirror.refresh();
128 this.code_mirror.refresh();
130 };
129 };
131
130
132
131
133 /**
132 /**
134 * should be overritten by subclass
133 * should be overritten by subclass
135 * @method edit
134 * @method edit
136 **/
135 **/
137 Cell.prototype.edit = function () {
136 Cell.prototype.edit = function () {
138 };
137 };
139
138
140
139
141 /**
140 /**
142 * should be overritten by subclass
141 * should be overritten by subclass
143 * @method render
142 * @method render
144 **/
143 **/
145 Cell.prototype.render = function () {
144 Cell.prototype.render = function () {
146 };
145 };
147
146
148 /**
147 /**
149 * should be overritten by subclass
148 * should be overritten by subclass
150 * serialise cell to json.
149 * serialise cell to json.
151 * @method toJSON
150 * @method toJSON
152 **/
151 **/
153 Cell.prototype.toJSON = function () {
152 Cell.prototype.toJSON = function () {
154 var data = {};
153 var data = {};
155 data.metadata = this.metadata;
154 data.metadata = this.metadata;
156 return data;
155 return data;
157 };
156 };
158
157
159
158
160 /**
159 /**
161 * should be overritten by subclass
160 * should be overritten by subclass
162 * @method fromJSON
161 * @method fromJSON
163 **/
162 **/
164 Cell.prototype.fromJSON = function (data) {
163 Cell.prototype.fromJSON = function (data) {
165 if (data.metadata !== undefined) {
164 if (data.metadata !== undefined) {
166 this.metadata = data.metadata;
165 this.metadata = data.metadata;
167 }
166 }
168 this.celltoolbar.rebuild();
167 this.celltoolbar.rebuild();
169 };
168 };
170
169
171
170
172 /**
171 /**
173 * can the cell be splitted in 2 cells.
172 * can the cell be splitted in 2 cells.
174 * @method is_splittable
173 * @method is_splittable
175 **/
174 **/
176 Cell.prototype.is_splittable = function () {
175 Cell.prototype.is_splittable = function () {
177 return true;
176 return true;
178 };
177 };
179
178
180
179
181 /**
180 /**
182 * @return {String} - the text before the cursor
181 * @return {String} - the text before the cursor
183 * @method get_pre_cursor
182 * @method get_pre_cursor
184 **/
183 **/
185 Cell.prototype.get_pre_cursor = function () {
184 Cell.prototype.get_pre_cursor = function () {
186 var cursor = this.code_mirror.getCursor();
185 var cursor = this.code_mirror.getCursor();
187 var text = this.code_mirror.getRange({line:0,ch:0}, cursor);
186 var text = this.code_mirror.getRange({line:0,ch:0}, cursor);
188 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
187 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
189 return text;
188 return text;
190 }
189 }
191
190
192
191
193 /**
192 /**
194 * @return {String} - the text after the cursor
193 * @return {String} - the text after the cursor
195 * @method get_post_cursor
194 * @method get_post_cursor
196 **/
195 **/
197 Cell.prototype.get_post_cursor = function () {
196 Cell.prototype.get_post_cursor = function () {
198 var cursor = this.code_mirror.getCursor();
197 var cursor = this.code_mirror.getCursor();
199 var last_line_num = this.code_mirror.lineCount()-1;
198 var last_line_num = this.code_mirror.lineCount()-1;
200 var last_line_len = this.code_mirror.getLine(last_line_num).length;
199 var last_line_len = this.code_mirror.getLine(last_line_num).length;
201 var end = {line:last_line_num, ch:last_line_len}
200 var end = {line:last_line_num, ch:last_line_len}
202 var text = this.code_mirror.getRange(cursor, end);
201 var text = this.code_mirror.getRange(cursor, end);
203 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
202 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
204 return text;
203 return text;
205 };
204 };
206
205
207
206
208 /** Grow the cell by hand. This is used upon reloading from JSON, when the
207 /** Grow the cell by hand. This is used upon reloading from JSON, when the
209 * autogrow handler is not called.
208 * autogrow handler is not called.
210 *
209 *
211 * could be made static
210 * could be made static
212 *
211 *
213 * @param {Dom element} - element
212 * @param {Dom element} - element
214 * @method grow
213 * @method grow
215 **/
214 **/
216 Cell.prototype.grow = function(element) {
215 Cell.prototype.grow = function(element) {
217 var dom = element.get(0);
216 var dom = element.get(0);
218 var lines_count = 0;
217 var lines_count = 0;
219 // modified split rule from
218 // modified split rule from
220 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
219 // http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424
221 var lines = dom.value.split(/\r|\r\n|\n/);
220 var lines = dom.value.split(/\r|\r\n|\n/);
222 lines_count = lines.length;
221 lines_count = lines.length;
223 if (lines_count >= 1) {
222 if (lines_count >= 1) {
224 dom.rows = lines_count;
223 dom.rows = lines_count;
225 } else {
224 } else {
226 dom.rows = 1;
225 dom.rows = 1;
227 }
226 }
228 };
227 };
229
228
230 /**
229 /**
231 * Toggle CodeMirror LineNumber
230 * Toggle CodeMirror LineNumber
232 * @method toggle_line_numbers
231 * @method toggle_line_numbers
233 **/
232 **/
234 Cell.prototype.toggle_line_numbers = function () {
233 Cell.prototype.toggle_line_numbers = function () {
235 if (this.code_mirror.getOption('lineNumbers') == false) {
234 if (this.code_mirror.getOption('lineNumbers') == false) {
236 this.code_mirror.setOption('lineNumbers', true);
235 this.code_mirror.setOption('lineNumbers', true);
237 } else {
236 } else {
238 this.code_mirror.setOption('lineNumbers', false);
237 this.code_mirror.setOption('lineNumbers', false);
239 }
238 }
240 this.code_mirror.refresh();
239 this.code_mirror.refresh();
241 };
240 };
242
241
243 /**
242 /**
244 * force codemirror highlight mode
243 * force codemirror highlight mode
245 * @method force_highlight
244 * @method force_highlight
246 * @param {object} - CodeMirror mode
245 * @param {object} - CodeMirror mode
247 **/
246 **/
248 Cell.prototype.force_highlight = function(mode) {
247 Cell.prototype.force_highlight = function(mode) {
249 this.user_highlight = mode;
248 this.user_highlight = mode;
250 this.auto_highlight();
249 this.auto_highlight();
251 };
250 };
252
251
253 /**
252 /**
254 * Try to autodetect cell highlight mode, or use selected mode
253 * Try to autodetect cell highlight mode, or use selected mode
255 * @methods _auto_highlight
254 * @methods _auto_highlight
256 * @private
255 * @private
257 * @param {String|object|undefined} - CodeMirror mode | 'auto'
256 * @param {String|object|undefined} - CodeMirror mode | 'auto'
258 **/
257 **/
259 Cell.prototype._auto_highlight = function (modes) {
258 Cell.prototype._auto_highlight = function (modes) {
260 //Here we handle manually selected modes
259 //Here we handle manually selected modes
261 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
260 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
262 {
261 {
263 var mode = this.user_highlight;
262 var mode = this.user_highlight;
264 CodeMirror.autoLoadMode(this.code_mirror, mode);
263 CodeMirror.autoLoadMode(this.code_mirror, mode);
265 this.code_mirror.setOption('mode', mode);
264 this.code_mirror.setOption('mode', mode);
266 return;
265 return;
267 }
266 }
268 var first_line = this.code_mirror.getLine(0);
267 var first_line = this.code_mirror.getLine(0);
269 // loop on every pairs
268 // loop on every pairs
270 for( var mode in modes) {
269 for( var mode in modes) {
271 var regs = modes[mode]['reg'];
270 var regs = modes[mode]['reg'];
272 // only one key every time but regexp can't be keys...
271 // only one key every time but regexp can't be keys...
273 for(var reg in regs ) {
272 for(var reg in regs ) {
274 // here we handle non magic_modes
273 // here we handle non magic_modes
275 if(first_line.match(regs[reg]) != null) {
274 if(first_line.match(regs[reg]) != null) {
276 if (mode.search('magic_') != 0) {
275 if (mode.search('magic_') != 0) {
277 this.code_mirror.setOption('mode',mode);
276 this.code_mirror.setOption('mode',mode);
278 CodeMirror.autoLoadMode(this.code_mirror, mode);
277 CodeMirror.autoLoadMode(this.code_mirror, mode);
279 return;
278 return;
280 }
279 }
281 var open = modes[mode]['open']|| "%%";
280 var open = modes[mode]['open']|| "%%";
282 var close = modes[mode]['close']|| "%%end";
281 var close = modes[mode]['close']|| "%%end";
283 var mmode = mode;
282 var mmode = mode;
284 mode = mmode.substr(6);
283 mode = mmode.substr(6);
285 CodeMirror.autoLoadMode(this.code_mirror, mode);
284 CodeMirror.autoLoadMode(this.code_mirror, mode);
286 // create on the fly a mode that swhitch between
285 // create on the fly a mode that swhitch between
287 // plain/text and smth else otherwise `%%` is
286 // plain/text and smth else otherwise `%%` is
288 // source of some highlight issues.
287 // source of some highlight issues.
289 // we use patchedGetMode to circumvent a bug in CM
288 // we use patchedGetMode to circumvent a bug in CM
290 CodeMirror.defineMode(mmode , function(config) {
289 CodeMirror.defineMode(mmode , function(config) {
291 return CodeMirror.multiplexingMode(
290 return CodeMirror.multiplexingMode(
292 CodeMirror.patchedGetMode(config, 'text/plain'),
291 CodeMirror.patchedGetMode(config, 'text/plain'),
293 // always set someting on close
292 // always set someting on close
294 {open: open, close: close,
293 {open: open, close: close,
295 mode: CodeMirror.patchedGetMode(config, mode),
294 mode: CodeMirror.patchedGetMode(config, mode),
296 delimStyle: "delimit"
295 delimStyle: "delimit"
297 }
296 }
298 );
297 );
299 });
298 });
300 this.code_mirror.setOption('mode', mmode);
299 this.code_mirror.setOption('mode', mmode);
301 return;
300 return;
302 }
301 }
303 }
302 }
304 }
303 }
305 // fallback on default (python)
304 // fallback on default (python)
306 var default_mode = this.default_mode || 'text/plain';
305 var default_mode = this.default_mode || 'text/plain';
307 this.code_mirror.setOption('mode', default_mode);
306 this.code_mirror.setOption('mode', default_mode);
308 };
307 };
309
308
310 IPython.Cell = Cell;
309 IPython.Cell = Cell;
311
310
312 return IPython;
311 return IPython;
313
312
314 }(IPython));
313 }(IPython));
315
314
@@ -1,358 +1,381 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2012 The IPython Development Team
2 // Copyright (C) 2012 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 // CellToolbar
9 // CellToolbar
10 //============================================================================
10 //============================================================================
11
11
12
12
13 /**
13 /**
14 * A Module to control the per-cell toolbar.
14 * A Module to control the per-cell toolbar.
15 * @module IPython
15 * @module IPython
16 * @namespace IPython
16 * @namespace IPython
17 * @submodule CellToolbar
17 * @submodule CellToolbar
18 */
18 */
19 var IPython = (function (IPython) {
19 var IPython = (function (IPython) {
20 "use strict";
20 "use strict";
21
21
22
23 /**
22 /**
24 * @constructor
23 * @constructor
25 * @class CellToolbar
24 * @class CellToolbar
26 * @param {The cell to attach the metadata UI to} cell
25 * @param {The cell to attach the metadata UI to} cell
27 */
26 */
28 var CellToolbar = function (cell) {
27 var CellToolbar = function (cell) {
29 CellToolbar._instances.push(this);
28 CellToolbar._instances.push(this);
30 this.inner_element = $('<div/>');
31 this.cell = cell;
29 this.cell = cell;
32 this.element = $('<div/>').addClass('celltoolbar')
30 this.create_element();
33 .append(this.inner_element)
34 this.rebuild();
31 this.rebuild();
35 return this;
32 return this;
36 };
33 };
37
34
35
36 CellToolbar.prototype.create_element = function () {
37 this.inner_element = $('<div/>');
38 var ctb_element = $('<div/>').addClass('celltoolbar')
39 .append(this.inner_element);
40 this.element = $('<div/>').addClass('ctb_wrapper hbox');
41 ctb_element.addClass('box-flex1');
42 var ctb_prompt = $('<div/>').addClass('ctb_prompt prompt');
43 this.element.append(ctb_prompt).append(ctb_element);
44 };
45
46
38 CellToolbar.dropdown_preset_element = $('<select/>')
47 CellToolbar.dropdown_preset_element = $('<select/>')
39 .addClass('ui-widget ui-widget-content')
48 .addClass('ui-widget ui-widget-content')
40 .attr('id', 'celltoolbar_selector')
49 .attr('id', 'celltoolbar_selector')
41 .append($('<option/>').attr('value', '').text('None'))
50 .append($('<option/>').attr('value', '').text('None'))
42
51
43 CellToolbar.dropdown_preset_element.change(function(){
52
53 CellToolbar.dropdown_preset_element.change(function() {
44 var val = CellToolbar.dropdown_preset_element.val()
54 var val = CellToolbar.dropdown_preset_element.val()
45 if(val ==''){
55 if(val ==''){
46 $('body').removeClass('celltoolbar-on')
56 CellToolbar.hide();
47 } else {
57 } else {
48 $('body').addClass('celltoolbar-on')
58 CellToolbar.show();
49 CellToolbar.activate_preset(val)
59 CellToolbar.activate_preset(val);
50 }
60 }
51 })
61 })
52
62
53
63
64 CellToolbar.hide = function () {
65 $('.ctb_wrapper').hide();
66 $('.input_area').addClass('no_input_radius');
67 $('.text_cell_input').addClass('no_input_radius');
68 }
69
70
71 CellToolbar.show = function () {
72 $('.ctb_wrapper').show();
73 $('.input_area').removeClass('no_input_radius');
74 $('.text_cell_input').removeClass('no_input_radius');
75 }
76
54
77
55 /**
78 /**
56 * Class variable that should contain a dict of all availlable callback
79 * Class variable that should contain a dict of all availlable callback
57 * we need to think of wether or not we allow nested namespace
80 * we need to think of wether or not we allow nested namespace
58 * @property _callback_dict
81 * @property _callback_dict
59 * @private
82 * @private
60 * @static
83 * @static
61 * @type Dict
84 * @type Dict
62 */
85 */
63 CellToolbar._callback_dict = {};
86 CellToolbar._callback_dict = {};
64
87
65 /**
88 /**
66 * Class variable that should contain the reverse order list of the button
89 * Class variable that should contain the reverse order list of the button
67 * to add to the toolbar of each cell
90 * to add to the toolbar of each cell
68 * @property _ui_controls_list
91 * @property _ui_controls_list
69 * @private
92 * @private
70 * @static
93 * @static
71 * @type List
94 * @type List
72 */
95 */
73 CellToolbar._ui_controls_list = [];
96 CellToolbar._ui_controls_list = [];
74
97
75 /**
98 /**
76 * Class variable that should contains the CellToolbar instances for each
99 * Class variable that should contains the CellToolbar instances for each
77 * cell of the notebook
100 * cell of the notebook
78 *
101 *
79 * @private
102 * @private
80 * @property _instances
103 * @property _instances
81 * @static
104 * @static
82 * @type List
105 * @type List
83 */
106 */
84 CellToolbar._instances =[]
107 CellToolbar._instances =[]
85
108
86 /**
109 /**
87 * keep a list of all the availlabel presets for the toolbar
110 * keep a list of all the availlabel presets for the toolbar
88 * @private
111 * @private
89 * @property _presets
112 * @property _presets
90 * @static
113 * @static
91 * @type Dict
114 * @type Dict
92 */
115 */
93 CellToolbar._presets ={}
116 CellToolbar._presets ={}
94
117
95 // this is by design not a prototype.
118 // this is by design not a prototype.
96 /**
119 /**
97 * Register a callback to create an UI element in a cell toolbar.
120 * Register a callback to create an UI element in a cell toolbar.
98 * @method register_callback
121 * @method register_callback
99 * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
122 * @param name {String} name to use to refer to the callback. It is advised to use a prefix with the name
100 * for easier sorting and avoid collision
123 * for easier sorting and avoid collision
101 * @param callback {function(div, cell)} callback that will be called to generate the ui element
124 * @param callback {function(div, cell)} callback that will be called to generate the ui element
102 *
125 *
103 *
126 *
104 * The callback will receive the following element :
127 * The callback will receive the following element :
105 *
128 *
106 * * a div in which to add element.
129 * * a div in which to add element.
107 * * the cell it is responsible from
130 * * the cell it is responsible from
108 *
131 *
109 * @example
132 * @example
110 *
133 *
111 * Example that create callback for a button that toggle between `true` and `false` label,
134 * Example that create callback for a button that toggle between `true` and `false` label,
112 * with the metadata under the key 'foo' to reflect the status of the button.
135 * with the metadata under the key 'foo' to reflect the status of the button.
113 *
136 *
114 * // first param reference to a DOM div
137 * // first param reference to a DOM div
115 * // second param reference to the cell.
138 * // second param reference to the cell.
116 * var toggle = function(div, cell) {
139 * var toggle = function(div, cell) {
117 * var button_container = $(div)
140 * var button_container = $(div)
118 *
141 *
119 * // let's create a button that show the current value of the metadata
142 * // let's create a button that show the current value of the metadata
120 * var button = $('<div/>').button({label:String(cell.metadata.foo)});
143 * var button = $('<div/>').button({label:String(cell.metadata.foo)});
121 *
144 *
122 * // On click, change the metadata value and update the button label
145 * // On click, change the metadata value and update the button label
123 * button.click(function(){
146 * button.click(function(){
124 * var v = cell.metadata.foo;
147 * var v = cell.metadata.foo;
125 * cell.metadata.foo = !v;
148 * cell.metadata.foo = !v;
126 * button.button("option", "label", String(!v));
149 * button.button("option", "label", String(!v));
127 * })
150 * })
128 *
151 *
129 * // add the button to the DOM div.
152 * // add the button to the DOM div.
130 * button_container.append(button);
153 * button_container.append(button);
131 * }
154 * }
132 *
155 *
133 * // now we register the callback under the name `foo` to give the
156 * // now we register the callback under the name `foo` to give the
134 * // user the ability to use it later
157 * // user the ability to use it later
135 * CellToolbar.register_callback('foo', toggle);
158 * CellToolbar.register_callback('foo', toggle);
136 */
159 */
137 CellToolbar.register_callback = function(name, callback){
160 CellToolbar.register_callback = function(name, callback){
138 // Overwrite if it already exists.
161 // Overwrite if it already exists.
139 CellToolbar._callback_dict[name] = callback;
162 CellToolbar._callback_dict[name] = callback;
140 };
163 };
141
164
142 /**
165 /**
143 * Register a preset of UI element in a cell toolbar.
166 * Register a preset of UI element in a cell toolbar.
144 * Not supported Yet.
167 * Not supported Yet.
145 * @method register_preset
168 * @method register_preset
146 * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
169 * @param name {String} name to use to refer to the preset. It is advised to use a prefix with the name
147 * for easier sorting and avoid collision
170 * for easier sorting and avoid collision
148 * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list
171 * @param preset_list {List of String} reverse order of the button in the toolbar. Each String of the list
149 * should correspond to a name of a registerd callback.
172 * should correspond to a name of a registerd callback.
150 *
173 *
151 * @private
174 * @private
152 * @example
175 * @example
153 *
176 *
154 * CellToolbar.register_callback('foo.c1', function(div, cell){...});
177 * CellToolbar.register_callback('foo.c1', function(div, cell){...});
155 * CellToolbar.register_callback('foo.c2', function(div, cell){...});
178 * CellToolbar.register_callback('foo.c2', function(div, cell){...});
156 * CellToolbar.register_callback('foo.c3', function(div, cell){...});
179 * CellToolbar.register_callback('foo.c3', function(div, cell){...});
157 * CellToolbar.register_callback('foo.c4', function(div, cell){...});
180 * CellToolbar.register_callback('foo.c4', function(div, cell){...});
158 * CellToolbar.register_callback('foo.c5', function(div, cell){...});
181 * CellToolbar.register_callback('foo.c5', function(div, cell){...});
159 *
182 *
160 * CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])
183 * CellToolbar.register_preset('foo.foo_preset1', ['foo.c1', 'foo.c2', 'foo.c5'])
161 * CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])
184 * CellToolbar.register_preset('foo.foo_preset2', ['foo.c4', 'foo.c5'])
162 */
185 */
163 CellToolbar.register_preset = function(name, preset_list){
186 CellToolbar.register_preset = function(name, preset_list){
164 CellToolbar._presets[name] = preset_list
187 CellToolbar._presets[name] = preset_list
165 CellToolbar.dropdown_preset_element.append(
188 CellToolbar.dropdown_preset_element.append(
166 $('<option/>').attr('value', name).text(name)
189 $('<option/>').attr('value', name).text(name)
167 )
190 )
168 }
191 }
169 /**
192 /**
170 * Activate an UI preset from `register_preset`
193 * Activate an UI preset from `register_preset`
171 *
194 *
172 * This does not update the selection UI.
195 * This does not update the selection UI.
173 *
196 *
174 * @method activate_preset
197 * @method activate_preset
175 * @param preset_name {String} string corresponding to the preset name
198 * @param preset_name {String} string corresponding to the preset name
176 *
199 *
177 * @static
200 * @static
178 * @private
201 * @private
179 * @example
202 * @example
180 *
203 *
181 * CellToolbar.activate_preset('foo.foo_preset1');
204 * CellToolbar.activate_preset('foo.foo_preset1');
182 */
205 */
183 CellToolbar.activate_preset= function(preset_name){
206 CellToolbar.activate_preset= function(preset_name){
184 var preset = CellToolbar._presets[preset_name];
207 var preset = CellToolbar._presets[preset_name];
185
208
186 if(preset != undefined){
209 if(preset != undefined){
187 CellToolbar._ui_controls_list = preset;
210 CellToolbar._ui_controls_list = preset;
188 CellToolbar.rebuild_all();
211 CellToolbar.rebuild_all();
189 }
212 }
190 }
213 }
191
214
192
215
193 // this is by design not a prototype.
216 // this is by design not a prototype.
194 /**
217 /**
195 * This should be called on the class and not on a instance as it will trigger
218 * This should be called on the class and not on a instance as it will trigger
196 * rebuild of all the instances.
219 * rebuild of all the instances.
197 * @method rebuild_all
220 * @method rebuild_all
198 * @static
221 * @static
199 *
222 *
200 */
223 */
201 CellToolbar.rebuild_all = function(){
224 CellToolbar.rebuild_all = function(){
202 for(var i in CellToolbar._instances){
225 for(var i in CellToolbar._instances){
203 CellToolbar._instances[i].rebuild();
226 CellToolbar._instances[i].rebuild();
204 }
227 }
205 }
228 }
206
229
207 /**
230 /**
208 * Rebuild all the button on the toolbar to update it's state.
231 * Rebuild all the button on the toolbar to update it's state.
209 * @method rebuild
232 * @method rebuild
210 */
233 */
211 CellToolbar.prototype.rebuild = function(){
234 CellToolbar.prototype.rebuild = function(){
212 // strip evrything from the div
235 // strip evrything from the div
213 // which is probabli metainner.
236 // which is probabli metainner.
214 // or this.element.
237 // or this.element.
215 this.inner_element.empty();
238 this.inner_element.empty();
216
239
217 var cdict = CellToolbar._callback_dict;
240 var cdict = CellToolbar._callback_dict;
218 var preset = CellToolbar._ui_controls_list;
241 var preset = CellToolbar._ui_controls_list;
219 // Yes we iterate on the class varaible, not the instance one.
242 // Yes we iterate on the class varaible, not the instance one.
220 for ( var index in CellToolbar._ui_controls_list){
243 for ( var index in CellToolbar._ui_controls_list){
221 var local_div = $('<div/>').addClass('button_container');
244 var local_div = $('<div/>').addClass('button_container');
222 // Note,
245 // Note,
223 // do this the other way, wrap in try/catch and don't append if any errors.
246 // do this the other way, wrap in try/catch and don't append if any errors.
224 this.inner_element.append(local_div)
247 this.inner_element.append(local_div)
225 cdict[preset[index]](local_div, this.cell)
248 cdict[preset[index]](local_div, this.cell)
226 }
249 }
227
250
228 }
251 }
229
252
230
253
231 /**
254 /**
232 */
255 */
233 CellToolbar.utils = {};
256 CellToolbar.utils = {};
234
257
235 /**
258 /**
236 * A utility function to generate bindings between a checkbox and cell/metadata
259 * A utility function to generate bindings between a checkbox and cell/metadata
237 * @method utils.checkbox_ui_generator
260 * @method utils.checkbox_ui_generator
238 * @static
261 * @static
239 *
262 *
240 * @param name {string} Label in front of the checkbox
263 * @param name {string} Label in front of the checkbox
241 * @param setter {function( cell, newValue )}
264 * @param setter {function( cell, newValue )}
242 * A setter method to set the newValue
265 * A setter method to set the newValue
243 * @param getter {function( cell )}
266 * @param getter {function( cell )}
244 * A getter methods which return the current value.
267 * A getter methods which return the current value.
245 *
268 *
246 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
269 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
247 *
270 *
248 * @example
271 * @example
249 *
272 *
250 * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
273 * An exmple that bind the subkey `slideshow.isSectionStart` to a checkbox with a `New Slide` label
251 *
274 *
252 * var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',
275 * var newSlide = CellToolbar.utils.checkbox_ui_generator('New Slide',
253 * // setter
276 * // setter
254 * function(cell, value){
277 * function(cell, value){
255 * // we check that the slideshow namespace exist and create it if needed
278 * // we check that the slideshow namespace exist and create it if needed
256 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
279 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
257 * // set the value
280 * // set the value
258 * cell.metadata.slideshow.isSectionStart = value
281 * cell.metadata.slideshow.isSectionStart = value
259 * },
282 * },
260 * //geter
283 * //geter
261 * function(cell){ var ns = cell.metadata.slideshow;
284 * function(cell){ var ns = cell.metadata.slideshow;
262 * // if the slideshow namespace does not exist return `undefined`
285 * // if the slideshow namespace does not exist return `undefined`
263 * // (will be interpreted as `false` by checkbox) otherwise
286 * // (will be interpreted as `false` by checkbox) otherwise
264 * // return the value
287 * // return the value
265 * return (ns == undefined)? undefined: ns.isSectionStart
288 * return (ns == undefined)? undefined: ns.isSectionStart
266 * }
289 * }
267 * );
290 * );
268 *
291 *
269 * CellToolbar.register_callback('newSlide', newSlide);
292 * CellToolbar.register_callback('newSlide', newSlide);
270 *
293 *
271 */
294 */
272 CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){
295 CellToolbar.utils.checkbox_ui_generator = function(name, setter, getter){
273 return function(div, cell) {
296 return function(div, cell) {
274 var button_container = $(div)
297 var button_container = $(div)
275
298
276 var chkb = $('<input/>').attr('type', 'checkbox');
299 var chkb = $('<input/>').attr('type', 'checkbox');
277 var lbl = $('<label/>').append($('<span/>').text(name).css('font-size', '77%'));
300 var lbl = $('<label/>').append($('<span/>').text(name));
278 lbl.append(chkb);
301 lbl.append(chkb);
279 chkb.attr("checked", getter(cell));
302 chkb.attr("checked", getter(cell));
280
303
281 chkb.click(function(){
304 chkb.click(function(){
282 var v = getter(cell);
305 var v = getter(cell);
283 setter(cell, !v);
306 setter(cell, !v);
284 chkb.attr("checked", !v);
307 chkb.attr("checked", !v);
285 })
308 })
286 button_container.append($('<div/>').append(lbl));
309 button_container.append($('<div/>').append(lbl));
287
310
288 }
311 }
289 }
312 }
290
313
291 /**
314 /**
292 * A utility function to generate bindings between a dropdown list cell
315 * A utility function to generate bindings between a dropdown list cell
293 * @method utils.select_ui_generator
316 * @method utils.select_ui_generator
294 * @static
317 * @static
295 *
318 *
296 * @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.
319 * @param list_list {list of sublist} List of sublist of metadata value and name in the dropdown list.
297 * subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,
320 * subslit shoud contain 2 element each, first a string that woul be displayed in the dropdown list,
298 * and second the corresponding value to be passed to setter/return by getter.
321 * and second the corresponding value to be passed to setter/return by getter.
299 * @param setter {function( cell, newValue )}
322 * @param setter {function( cell, newValue )}
300 * A setter method to set the newValue
323 * A setter method to set the newValue
301 * @param getter {function( cell )}
324 * @param getter {function( cell )}
302 * A getter methods which return the current value of the metadata.
325 * A getter methods which return the current value of the metadata.
303 * @param [label=""] {String} optionnal label for the dropdown menu
326 * @param [label=""] {String} optionnal label for the dropdown menu
304 *
327 *
305 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
328 * @return callback {function( div, cell )} Callback to be passed to `register_callback`
306 *
329 *
307 * @example
330 * @example
308 *
331 *
309 * var select_type = CellToolbar.utils.select_ui_generator([
332 * var select_type = CellToolbar.utils.select_ui_generator([
310 * ["<None>" , undefined ],
333 * ["<None>" , undefined ],
311 * ["Header Slide" , "header_slide" ],
334 * ["Header Slide" , "header_slide" ],
312 * ["Slide" , "slide" ],
335 * ["Slide" , "slide" ],
313 * ["Fragment" , "fragment" ],
336 * ["Fragment" , "fragment" ],
314 * ["Skip" , "skip" ],
337 * ["Skip" , "skip" ],
315 * ],
338 * ],
316 * // setter
339 * // setter
317 * function(cell, value){
340 * function(cell, value){
318 * // we check that the slideshow namespace exist and create it if needed
341 * // we check that the slideshow namespace exist and create it if needed
319 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
342 * if (cell.metadata.slideshow == undefined){cell.metadata.slideshow = {}}
320 * // set the value
343 * // set the value
321 * cell.metadata.slideshow.slide_type = value
344 * cell.metadata.slideshow.slide_type = value
322 * },
345 * },
323 * //geter
346 * //geter
324 * function(cell){ var ns = cell.metadata.slideshow;
347 * function(cell){ var ns = cell.metadata.slideshow;
325 * // if the slideshow namespace does not exist return `undefined`
348 * // if the slideshow namespace does not exist return `undefined`
326 * // (will be interpreted as `false` by checkbox) otherwise
349 * // (will be interpreted as `false` by checkbox) otherwise
327 * // return the value
350 * // return the value
328 * return (ns == undefined)? undefined: ns.slide_type
351 * return (ns == undefined)? undefined: ns.slide_type
329 * }
352 * }
330 * CellToolbar.register_callback('slideshow.select', select_type);
353 * CellToolbar.register_callback('slideshow.select', select_type);
331 *
354 *
332 */
355 */
333 CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label){
356 CellToolbar.utils.select_ui_generator = function(list_list, setter, getter, label){
334 label= label? label: "";
357 label= label? label: "";
335 return function(div, cell) {
358 return function(div, cell) {
336 var button_container = $(div)
359 var button_container = $(div)
337 var lbl = $("<label/>").append($('<span/>').text(label).css('font-size', '77%'));
360 var lbl = $("<label/>").append($('<span/>').text(label));
338 var select = $('<select/>');
361 var select = $('<select/>').addClass('ui-widget ui-widget-content');
339 for(var itemn in list_list){
362 for(var itemn in list_list){
340 var opt = $('<option/>');
363 var opt = $('<option/>');
341 opt.attr('value', list_list[itemn][1])
364 opt.attr('value', list_list[itemn][1])
342 opt.text(list_list[itemn][0])
365 opt.text(list_list[itemn][0])
343 select.append(opt);
366 select.append(opt);
344 }
367 }
345 select.val(getter(cell));
368 select.val(getter(cell));
346 select.change(function(){
369 select.change(function(){
347 setter(cell, select.val());
370 setter(cell, select.val());
348 });
371 });
349 button_container.append($('<div/>').append(lbl).append(select));
372 button_container.append($('<div/>').append(lbl).append(select));
350
373
351 }
374 }
352 };
375 };
353
376
354
377
355 IPython.CellToolbar = CellToolbar;
378 IPython.CellToolbar = CellToolbar;
356
379
357 return IPython;
380 return IPython;
358 }(IPython));
381 }(IPython));
@@ -1,381 +1,384 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 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 var key = IPython.utils.keycodes;
22 var key = IPython.utils.keycodes;
23 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
23 CodeMirror.modeURL = "/static/codemirror/mode/%N/%N.js";
24
24
25 /**
25 /**
26 * A Cell conceived to write code.
26 * A Cell conceived to write code.
27 *
27 *
28 * The kernel doesn't have to be set at creation time, in that case
28 * The kernel doesn't have to be set at creation time, in that case
29 * it will be null and set_kernel has to be called later.
29 * it will be null and set_kernel has to be called later.
30 * @class CodeCell
30 * @class CodeCell
31 * @extends IPython.Cell
31 * @extends IPython.Cell
32 *
32 *
33 * @constructor
33 * @constructor
34 * @param {Object|null} kernel
34 * @param {Object|null} kernel
35 */
35 */
36 var CodeCell = function (kernel) {
36 var CodeCell = function (kernel) {
37 this.kernel = kernel || null;
37 this.kernel = kernel || null;
38 this.code_mirror = null;
38 this.code_mirror = null;
39 this.input_prompt_number = null;
39 this.input_prompt_number = null;
40 this.collapsed = false;
40 this.collapsed = false;
41 this.default_mode = 'python';
41 this.default_mode = 'python';
42 IPython.Cell.apply(this, arguments);
42 IPython.Cell.apply(this, arguments);
43
43
44 var that = this;
44 var that = this;
45 this.element.focusout(
45 this.element.focusout(
46 function() { that.auto_highlight(); }
46 function() { that.auto_highlight(); }
47 );
47 );
48 };
48 };
49
49
50 CodeCell.prototype = new IPython.Cell();
50 CodeCell.prototype = new IPython.Cell();
51
51
52 /**
52 /**
53 * @method auto_highlight
53 * @method auto_highlight
54 */
54 */
55 CodeCell.prototype.auto_highlight = function () {
55 CodeCell.prototype.auto_highlight = function () {
56 this._auto_highlight(IPython.config.cell_magic_highlight)
56 this._auto_highlight(IPython.config.cell_magic_highlight)
57 };
57 };
58
58
59 /** @method create_element */
59 /** @method create_element */
60 CodeCell.prototype.create_element = function () {
60 CodeCell.prototype.create_element = function () {
61 IPython.Cell.prototype.create_element.apply(this, arguments);
61 IPython.Cell.prototype.create_element.apply(this, arguments);
62
62
63 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
63 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
64 cell.append(this.celltoolbar.element);
65 cell.attr('tabindex','2');
64 cell.attr('tabindex','2');
65
66 this.celltoolbar = new IPython.CellToolbar(this);
67 cell.append(this.celltoolbar.element);
68
66 var input = $('<div></div>').addClass('input hbox');
69 var input = $('<div></div>').addClass('input hbox');
67 input.append($('<div/>').addClass('prompt input_prompt'));
70 input.append($('<div/>').addClass('prompt input_prompt'));
68 var input_area = $('<div/>').addClass('input_area box-flex1');
71 var input_area = $('<div/>').addClass('input_area box-flex1');
69 this.code_mirror = CodeMirror(input_area.get(0), {
72 this.code_mirror = CodeMirror(input_area.get(0), {
70 indentUnit : 4,
73 indentUnit : 4,
71 mode: 'python',
74 mode: 'python',
72 theme: 'ipython',
75 theme: 'ipython',
73 readOnly: this.read_only,
76 readOnly: this.read_only,
74 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
77 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
75 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this),
78 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this),
76 matchBrackets: true
79 matchBrackets: true
77 });
80 });
78 input.append(input_area);
81 input.append(input_area);
79 var output = $('<div></div>');
82 var output = $('<div></div>');
80 cell.append(input).append(output);
83 cell.append(input).append(output);
81 this.element = cell;
84 this.element = cell;
82 this.output_area = new IPython.OutputArea(output, true);
85 this.output_area = new IPython.OutputArea(output, true);
83
86
84 // construct a completer only if class exist
87 // construct a completer only if class exist
85 // otherwise no print view
88 // otherwise no print view
86 if (IPython.Completer !== undefined)
89 if (IPython.Completer !== undefined)
87 {
90 {
88 this.completer = new IPython.Completer(this);
91 this.completer = new IPython.Completer(this);
89 }
92 }
90 };
93 };
91
94
92 /**
95 /**
93 * This method gets called in CodeMirror's onKeyDown/onKeyPress
96 * This method gets called in CodeMirror's onKeyDown/onKeyPress
94 * handlers and is used to provide custom key handling. Its return
97 * handlers and is used to provide custom key handling. Its return
95 * value is used to determine if CodeMirror should ignore the event:
98 * value is used to determine if CodeMirror should ignore the event:
96 * true = ignore, false = don't ignore.
99 * true = ignore, false = don't ignore.
97 * @method handle_codemirror_keyevent
100 * @method handle_codemirror_keyevent
98 */
101 */
99 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
102 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
100
103
101 if (this.read_only){
104 if (this.read_only){
102 return false;
105 return false;
103 }
106 }
104
107
105 var that = this;
108 var that = this;
106 // whatever key is pressed, first, cancel the tooltip request before
109 // whatever key is pressed, first, cancel the tooltip request before
107 // they are sent, and remove tooltip if any, except for tab again
110 // they are sent, and remove tooltip if any, except for tab again
108 if (event.type === 'keydown' && event.which != key.TAB ) {
111 if (event.type === 'keydown' && event.which != key.TAB ) {
109 IPython.tooltip.remove_and_cancel_tooltip();
112 IPython.tooltip.remove_and_cancel_tooltip();
110 };
113 };
111
114
112 var cur = editor.getCursor();
115 var cur = editor.getCursor();
113 if (event.keyCode === key.ENTER){
116 if (event.keyCode === key.ENTER){
114 this.auto_highlight();
117 this.auto_highlight();
115 }
118 }
116
119
117 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
120 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
118 // Always ignore shift-enter in CodeMirror as we handle it.
121 // Always ignore shift-enter in CodeMirror as we handle it.
119 return true;
122 return true;
120 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
123 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
121 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
124 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
122 // browser and keyboard layout !
125 // browser and keyboard layout !
123 // Pressing '(' , request tooltip, don't forget to reappend it
126 // Pressing '(' , request tooltip, don't forget to reappend it
124 IPython.tooltip.pending(that);
127 IPython.tooltip.pending(that);
125 } else if (event.which === key.UPARROW && event.type === 'keydown') {
128 } else if (event.which === key.UPARROW && event.type === 'keydown') {
126 // If we are not at the top, let CM handle the up arrow and
129 // If we are not at the top, let CM handle the up arrow and
127 // prevent the global keydown handler from handling it.
130 // prevent the global keydown handler from handling it.
128 if (!that.at_top()) {
131 if (!that.at_top()) {
129 event.stop();
132 event.stop();
130 return false;
133 return false;
131 } else {
134 } else {
132 return true;
135 return true;
133 };
136 };
134 } else if (event.which === key.ESC) {
137 } else if (event.which === key.ESC) {
135 IPython.tooltip.remove_and_cancel_tooltip(true);
138 IPython.tooltip.remove_and_cancel_tooltip(true);
136 return true;
139 return true;
137 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
140 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
138 // If we are not at the bottom, let CM handle the down arrow and
141 // If we are not at the bottom, let CM handle the down arrow and
139 // prevent the global keydown handler from handling it.
142 // prevent the global keydown handler from handling it.
140 if (!that.at_bottom()) {
143 if (!that.at_bottom()) {
141 event.stop();
144 event.stop();
142 return false;
145 return false;
143 } else {
146 } else {
144 return true;
147 return true;
145 };
148 };
146 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
149 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
147 if (editor.somethingSelected()){
150 if (editor.somethingSelected()){
148 var anchor = editor.getCursor("anchor");
151 var anchor = editor.getCursor("anchor");
149 var head = editor.getCursor("head");
152 var head = editor.getCursor("head");
150 if( anchor.line != head.line){
153 if( anchor.line != head.line){
151 return false;
154 return false;
152 }
155 }
153 }
156 }
154 IPython.tooltip.request(that);
157 IPython.tooltip.request(that);
155 event.stop();
158 event.stop();
156 return true;
159 return true;
157 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
160 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
158 // Tab completion.
161 // Tab completion.
159 //Do not trim here because of tooltip
162 //Do not trim here because of tooltip
160 if (editor.somethingSelected()){return false}
163 if (editor.somethingSelected()){return false}
161 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
164 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
162 if (pre_cursor.trim() === "") {
165 if (pre_cursor.trim() === "") {
163 // Don't autocomplete if the part of the line before the cursor
166 // Don't autocomplete if the part of the line before the cursor
164 // is empty. In this case, let CodeMirror handle indentation.
167 // is empty. In this case, let CodeMirror handle indentation.
165 return false;
168 return false;
166 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
169 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
167 IPython.tooltip.request(that);
170 IPython.tooltip.request(that);
168 // Prevent the event from bubbling up.
171 // Prevent the event from bubbling up.
169 event.stop();
172 event.stop();
170 // Prevent CodeMirror from handling the tab.
173 // Prevent CodeMirror from handling the tab.
171 return true;
174 return true;
172 } else {
175 } else {
173 event.stop();
176 event.stop();
174 this.completer.startCompletion();
177 this.completer.startCompletion();
175 return true;
178 return true;
176 };
179 };
177 } else {
180 } else {
178 // keypress/keyup also trigger on TAB press, and we don't want to
181 // keypress/keyup also trigger on TAB press, and we don't want to
179 // use those to disable tab completion.
182 // use those to disable tab completion.
180 return false;
183 return false;
181 };
184 };
182 return false;
185 return false;
183 };
186 };
184
187
185
188
186 // Kernel related calls.
189 // Kernel related calls.
187
190
188 CodeCell.prototype.set_kernel = function (kernel) {
191 CodeCell.prototype.set_kernel = function (kernel) {
189 this.kernel = kernel;
192 this.kernel = kernel;
190 }
193 }
191
194
192 /**
195 /**
193 * Execute current code cell to the kernel
196 * Execute current code cell to the kernel
194 * @method execute
197 * @method execute
195 */
198 */
196 CodeCell.prototype.execute = function () {
199 CodeCell.prototype.execute = function () {
197 this.output_area.clear_output(true, true, true);
200 this.output_area.clear_output(true, true, true);
198 this.set_input_prompt('*');
201 this.set_input_prompt('*');
199 this.element.addClass("running");
202 this.element.addClass("running");
200 var callbacks = {
203 var callbacks = {
201 'execute_reply': $.proxy(this._handle_execute_reply, this),
204 'execute_reply': $.proxy(this._handle_execute_reply, this),
202 'output': $.proxy(this.output_area.handle_output, this.output_area),
205 'output': $.proxy(this.output_area.handle_output, this.output_area),
203 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
206 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
204 'set_next_input': $.proxy(this._handle_set_next_input, this)
207 'set_next_input': $.proxy(this._handle_set_next_input, this)
205 };
208 };
206 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
209 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
207 };
210 };
208
211
209 /**
212 /**
210 * @method _handle_execute_reply
213 * @method _handle_execute_reply
211 * @private
214 * @private
212 */
215 */
213 CodeCell.prototype._handle_execute_reply = function (content) {
216 CodeCell.prototype._handle_execute_reply = function (content) {
214 this.set_input_prompt(content.execution_count);
217 this.set_input_prompt(content.execution_count);
215 this.element.removeClass("running");
218 this.element.removeClass("running");
216 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
219 $([IPython.events]).trigger('set_dirty.Notebook', {'value': true});
217 }
220 }
218
221
219 CodeCell.prototype._handle_set_next_input = function (text) {
222 CodeCell.prototype._handle_set_next_input = function (text) {
220 var data = {'cell': this, 'text': text}
223 var data = {'cell': this, 'text': text}
221 $([IPython.events]).trigger('set_next_input.Notebook', data);
224 $([IPython.events]).trigger('set_next_input.Notebook', data);
222 }
225 }
223
226
224 // Basic cell manipulation.
227 // Basic cell manipulation.
225
228
226 CodeCell.prototype.select = function () {
229 CodeCell.prototype.select = function () {
227 IPython.Cell.prototype.select.apply(this);
230 IPython.Cell.prototype.select.apply(this);
228 this.code_mirror.refresh();
231 this.code_mirror.refresh();
229 this.code_mirror.focus();
232 this.code_mirror.focus();
230 this.auto_highlight();
233 this.auto_highlight();
231 // We used to need an additional refresh() after the focus, but
234 // We used to need an additional refresh() after the focus, but
232 // it appears that this has been fixed in CM. This bug would show
235 // it appears that this has been fixed in CM. This bug would show
233 // up on FF when a newly loaded markdown cell was edited.
236 // up on FF when a newly loaded markdown cell was edited.
234 };
237 };
235
238
236
239
237 CodeCell.prototype.select_all = function () {
240 CodeCell.prototype.select_all = function () {
238 var start = {line: 0, ch: 0};
241 var start = {line: 0, ch: 0};
239 var nlines = this.code_mirror.lineCount();
242 var nlines = this.code_mirror.lineCount();
240 var last_line = this.code_mirror.getLine(nlines-1);
243 var last_line = this.code_mirror.getLine(nlines-1);
241 var end = {line: nlines-1, ch: last_line.length};
244 var end = {line: nlines-1, ch: last_line.length};
242 this.code_mirror.setSelection(start, end);
245 this.code_mirror.setSelection(start, end);
243 };
246 };
244
247
245
248
246 CodeCell.prototype.collapse = function () {
249 CodeCell.prototype.collapse = function () {
247 this.collapsed = true;
250 this.collapsed = true;
248 this.output_area.collapse();
251 this.output_area.collapse();
249 };
252 };
250
253
251
254
252 CodeCell.prototype.expand = function () {
255 CodeCell.prototype.expand = function () {
253 this.collapsed = false;
256 this.collapsed = false;
254 this.output_area.expand();
257 this.output_area.expand();
255 };
258 };
256
259
257
260
258 CodeCell.prototype.toggle_output = function () {
261 CodeCell.prototype.toggle_output = function () {
259 this.collapsed = Boolean(1 - this.collapsed);
262 this.collapsed = Boolean(1 - this.collapsed);
260 this.output_area.toggle_output();
263 this.output_area.toggle_output();
261 };
264 };
262
265
263
266
264 CodeCell.prototype.toggle_output_scroll = function () {
267 CodeCell.prototype.toggle_output_scroll = function () {
265 this.output_area.toggle_scroll();
268 this.output_area.toggle_scroll();
266 };
269 };
267
270
268
271
269 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
272 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
270 var ns = prompt_value || "&nbsp;";
273 var ns = prompt_value || "&nbsp;";
271 return 'In&nbsp;[' + ns + ']:'
274 return 'In&nbsp;[' + ns + ']:'
272 };
275 };
273
276
274 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
277 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
275 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
278 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
276 for(var i=1; i < lines_number; i++){html.push(['...:'])};
279 for(var i=1; i < lines_number; i++){html.push(['...:'])};
277 return html.join('</br>')
280 return html.join('</br>')
278 };
281 };
279
282
280 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
283 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
281
284
282
285
283 CodeCell.prototype.set_input_prompt = function (number) {
286 CodeCell.prototype.set_input_prompt = function (number) {
284 var nline = 1
287 var nline = 1
285 if( this.code_mirror != undefined) {
288 if( this.code_mirror != undefined) {
286 nline = this.code_mirror.lineCount();
289 nline = this.code_mirror.lineCount();
287 }
290 }
288 this.input_prompt_number = number;
291 this.input_prompt_number = number;
289 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
292 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
290 this.element.find('div.input_prompt').html(prompt_html);
293 this.element.find('div.input_prompt').html(prompt_html);
291 };
294 };
292
295
293
296
294 CodeCell.prototype.clear_input = function () {
297 CodeCell.prototype.clear_input = function () {
295 this.code_mirror.setValue('');
298 this.code_mirror.setValue('');
296 };
299 };
297
300
298
301
299 CodeCell.prototype.get_text = function () {
302 CodeCell.prototype.get_text = function () {
300 return this.code_mirror.getValue();
303 return this.code_mirror.getValue();
301 };
304 };
302
305
303
306
304 CodeCell.prototype.set_text = function (code) {
307 CodeCell.prototype.set_text = function (code) {
305 return this.code_mirror.setValue(code);
308 return this.code_mirror.setValue(code);
306 };
309 };
307
310
308
311
309 CodeCell.prototype.at_top = function () {
312 CodeCell.prototype.at_top = function () {
310 var cursor = this.code_mirror.getCursor();
313 var cursor = this.code_mirror.getCursor();
311 if (cursor.line === 0 && cursor.ch === 0) {
314 if (cursor.line === 0 && cursor.ch === 0) {
312 return true;
315 return true;
313 } else {
316 } else {
314 return false;
317 return false;
315 }
318 }
316 };
319 };
317
320
318
321
319 CodeCell.prototype.at_bottom = function () {
322 CodeCell.prototype.at_bottom = function () {
320 var cursor = this.code_mirror.getCursor();
323 var cursor = this.code_mirror.getCursor();
321 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
324 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
322 return true;
325 return true;
323 } else {
326 } else {
324 return false;
327 return false;
325 }
328 }
326 };
329 };
327
330
328
331
329 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
332 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
330 this.output_area.clear_output(stdout, stderr, other);
333 this.output_area.clear_output(stdout, stderr, other);
331 };
334 };
332
335
333
336
334 // JSON serialization
337 // JSON serialization
335
338
336 CodeCell.prototype.fromJSON = function (data) {
339 CodeCell.prototype.fromJSON = function (data) {
337 IPython.Cell.prototype.fromJSON.apply(this, arguments);
340 IPython.Cell.prototype.fromJSON.apply(this, arguments);
338 if (data.cell_type === 'code') {
341 if (data.cell_type === 'code') {
339 if (data.input !== undefined) {
342 if (data.input !== undefined) {
340 this.set_text(data.input);
343 this.set_text(data.input);
341 // make this value the starting point, so that we can only undo
344 // make this value the starting point, so that we can only undo
342 // to this state, instead of a blank cell
345 // to this state, instead of a blank cell
343 this.code_mirror.clearHistory();
346 this.code_mirror.clearHistory();
344 this.auto_highlight();
347 this.auto_highlight();
345 }
348 }
346 if (data.prompt_number !== undefined) {
349 if (data.prompt_number !== undefined) {
347 this.set_input_prompt(data.prompt_number);
350 this.set_input_prompt(data.prompt_number);
348 } else {
351 } else {
349 this.set_input_prompt();
352 this.set_input_prompt();
350 };
353 };
351 this.output_area.fromJSON(data.outputs);
354 this.output_area.fromJSON(data.outputs);
352 if (data.collapsed !== undefined) {
355 if (data.collapsed !== undefined) {
353 if (data.collapsed) {
356 if (data.collapsed) {
354 this.collapse();
357 this.collapse();
355 } else {
358 } else {
356 this.expand();
359 this.expand();
357 };
360 };
358 };
361 };
359 };
362 };
360 };
363 };
361
364
362
365
363 CodeCell.prototype.toJSON = function () {
366 CodeCell.prototype.toJSON = function () {
364 var data = IPython.Cell.prototype.toJSON.apply(this);
367 var data = IPython.Cell.prototype.toJSON.apply(this);
365 data.input = this.get_text();
368 data.input = this.get_text();
366 data.cell_type = 'code';
369 data.cell_type = 'code';
367 if (this.input_prompt_number) {
370 if (this.input_prompt_number) {
368 data.prompt_number = this.input_prompt_number;
371 data.prompt_number = this.input_prompt_number;
369 };
372 };
370 var outputs = this.output_area.toJSON();
373 var outputs = this.output_area.toJSON();
371 data.outputs = outputs;
374 data.outputs = outputs;
372 data.language = 'python';
375 data.language = 'python';
373 data.collapsed = this.collapsed;
376 data.collapsed = this.collapsed;
374 return data;
377 return data;
375 };
378 };
376
379
377
380
378 IPython.CodeCell = CodeCell;
381 IPython.CodeCell = CodeCell;
379
382
380 return IPython;
383 return IPython;
381 }(IPython));
384 }(IPython));
@@ -1,533 +1,534 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Copyright (C) 2008-2012 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 // TextCell
9 // TextCell
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 A module that allow to create different type of Text Cell
13 A module that allow to create different type of Text Cell
14 @module IPython
14 @module IPython
15 @namespace IPython
15 @namespace IPython
16 */
16 */
17 var IPython = (function (IPython) {
17 var IPython = (function (IPython) {
18
18
19 // TextCell base class
19 // TextCell base class
20 var key = IPython.utils.keycodes;
20 var key = IPython.utils.keycodes;
21
21
22 /**
22 /**
23 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
23 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
24 * cell start as not redered.
24 * cell start as not redered.
25 *
25 *
26 * @class TextCell
26 * @class TextCell
27 * @constructor TextCell
27 * @constructor TextCell
28 * @extend Ipython.Cell
28 * @extend Ipython.Cell
29 */
29 */
30 var TextCell = function () {
30 var TextCell = function () {
31 this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
31 this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed';
32 IPython.Cell.apply(this, arguments);
32 IPython.Cell.apply(this, arguments);
33 this.rendered = false;
33 this.rendered = false;
34 this.cell_type = this.cell_type || 'text';
34 this.cell_type = this.cell_type || 'text';
35 };
35 };
36
36
37 TextCell.prototype = new IPython.Cell();
37 TextCell.prototype = new IPython.Cell();
38
38
39 /**
39 /**
40 * Create the DOM element of the TextCell
40 * Create the DOM element of the TextCell
41 * @method create_element
41 * @method create_element
42 * @private
42 * @private
43 */
43 */
44 TextCell.prototype.create_element = function () {
44 TextCell.prototype.create_element = function () {
45 IPython.Cell.prototype.create_element.apply(this, arguments);
45 IPython.Cell.prototype.create_element.apply(this, arguments);
46 var cell = $("<div>").addClass('cell text_cell border-box-sizing vbox');
46 var cell = $("<div>").addClass('cell text_cell border-box-sizing vbox');
47 cell.append(this.celltoolbar.element);
48 cell.attr('tabindex','2');
47 cell.attr('tabindex','2');
48 this.celltoolbar = new IPython.CellToolbar(this);
49 cell.append(this.celltoolbar.element);
49 var input_area = $('<div/>').addClass('text_cell_input border-box-sizing');
50 var input_area = $('<div/>').addClass('text_cell_input border-box-sizing');
50 this.code_mirror = CodeMirror(input_area.get(0), {
51 this.code_mirror = CodeMirror(input_area.get(0), {
51 indentUnit : 4,
52 indentUnit : 4,
52 mode: this.code_mirror_mode,
53 mode: this.code_mirror_mode,
53 theme: 'default',
54 theme: 'default',
54 value: this.placeholder,
55 value: this.placeholder,
55 readOnly: this.read_only,
56 readOnly: this.read_only,
56 lineWrapping : true,
57 lineWrapping : true,
57 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
58 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
58 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
59 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
59 });
60 });
60 // The tabindex=-1 makes this div focusable.
61 // The tabindex=-1 makes this div focusable.
61 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
62 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
62 addClass('rendered_html').attr('tabindex','-1');
63 addClass('rendered_html').attr('tabindex','-1');
63 cell.append(input_area).append(render_area);
64 cell.append(input_area).append(render_area);
64 this.element = cell;
65 this.element = cell;
65 };
66 };
66
67
67
68
68 /**
69 /**
69 * Bind the DOM evet to cell actions
70 * Bind the DOM evet to cell actions
70 * Need to be called after TextCell.create_element
71 * Need to be called after TextCell.create_element
71 * @private
72 * @private
72 * @method bind_event
73 * @method bind_event
73 */
74 */
74 TextCell.prototype.bind_events = function () {
75 TextCell.prototype.bind_events = function () {
75 IPython.Cell.prototype.bind_events.apply(this);
76 IPython.Cell.prototype.bind_events.apply(this);
76 var that = this;
77 var that = this;
77 this.element.keydown(function (event) {
78 this.element.keydown(function (event) {
78 if (event.which === 13 && !event.shiftKey) {
79 if (event.which === 13 && !event.shiftKey) {
79 if (that.rendered) {
80 if (that.rendered) {
80 that.edit();
81 that.edit();
81 return false;
82 return false;
82 };
83 };
83 };
84 };
84 });
85 });
85 this.element.dblclick(function () {
86 this.element.dblclick(function () {
86 that.edit();
87 that.edit();
87 });
88 });
88 };
89 };
89
90
90 /**
91 /**
91 * This method gets called in CodeMirror's onKeyDown/onKeyPress
92 * This method gets called in CodeMirror's onKeyDown/onKeyPress
92 * handlers and is used to provide custom key handling.
93 * handlers and is used to provide custom key handling.
93 *
94 *
94 * Subclass should override this method to have custom handeling
95 * Subclass should override this method to have custom handeling
95 *
96 *
96 * @method handle_codemirror_keyevent
97 * @method handle_codemirror_keyevent
97 * @param {CodeMirror} editor - The codemirror instance bound to the cell
98 * @param {CodeMirror} editor - The codemirror instance bound to the cell
98 * @param {event} event -
99 * @param {event} event -
99 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
100 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
100 */
101 */
101 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
102 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
102
103
103 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
104 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
104 // Always ignore shift-enter in CodeMirror as we handle it.
105 // Always ignore shift-enter in CodeMirror as we handle it.
105 return true;
106 return true;
106 }
107 }
107 return false;
108 return false;
108 };
109 };
109
110
110 /**
111 /**
111 * Select the current cell and trigger 'focus'
112 * Select the current cell and trigger 'focus'
112 * @method select
113 * @method select
113 */
114 */
114 TextCell.prototype.select = function () {
115 TextCell.prototype.select = function () {
115 IPython.Cell.prototype.select.apply(this);
116 IPython.Cell.prototype.select.apply(this);
116 var output = this.element.find("div.text_cell_render");
117 var output = this.element.find("div.text_cell_render");
117 output.trigger('focus');
118 output.trigger('focus');
118 };
119 };
119
120
120 /**
121 /**
121 * unselect the current cell and `render` it
122 * unselect the current cell and `render` it
122 * @method unselect
123 * @method unselect
123 */
124 */
124 TextCell.prototype.unselect = function() {
125 TextCell.prototype.unselect = function() {
125 // render on selection of another cell
126 // render on selection of another cell
126 this.render();
127 this.render();
127 IPython.Cell.prototype.unselect.apply(this);
128 IPython.Cell.prototype.unselect.apply(this);
128 };
129 };
129
130
130 /**
131 /**
131 *
132 *
132 * put the current cell in edition mode
133 * put the current cell in edition mode
133 * @method edit
134 * @method edit
134 */
135 */
135 TextCell.prototype.edit = function () {
136 TextCell.prototype.edit = function () {
136 if ( this.read_only ) return;
137 if ( this.read_only ) return;
137 if (this.rendered === true) {
138 if (this.rendered === true) {
138 var text_cell = this.element;
139 var text_cell = this.element;
139 var output = text_cell.find("div.text_cell_render");
140 var output = text_cell.find("div.text_cell_render");
140 output.hide();
141 output.hide();
141 text_cell.find('div.text_cell_input').show();
142 text_cell.find('div.text_cell_input').show();
142 this.code_mirror.refresh();
143 this.code_mirror.refresh();
143 this.code_mirror.focus();
144 this.code_mirror.focus();
144 // We used to need an additional refresh() after the focus, but
145 // We used to need an additional refresh() after the focus, but
145 // it appears that this has been fixed in CM. This bug would show
146 // it appears that this has been fixed in CM. This bug would show
146 // up on FF when a newly loaded markdown cell was edited.
147 // up on FF when a newly loaded markdown cell was edited.
147 this.rendered = false;
148 this.rendered = false;
148 if (this.get_text() === this.placeholder) {
149 if (this.get_text() === this.placeholder) {
149 this.set_text('');
150 this.set_text('');
150 this.refresh();
151 this.refresh();
151 }
152 }
152 }
153 }
153 };
154 };
154
155
155
156
156 /**
157 /**
157 * Empty, Subclasses must define render.
158 * Empty, Subclasses must define render.
158 * @method render
159 * @method render
159 */
160 */
160 TextCell.prototype.render = function () {};
161 TextCell.prototype.render = function () {};
161
162
162
163
163 /**
164 /**
164 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
165 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
165 * @method get_text
166 * @method get_text
166 * @retrun {string} CodeMirror current text value
167 * @retrun {string} CodeMirror current text value
167 */
168 */
168 TextCell.prototype.get_text = function() {
169 TextCell.prototype.get_text = function() {
169 return this.code_mirror.getValue();
170 return this.code_mirror.getValue();
170 };
171 };
171
172
172 /**
173 /**
173 * @param {string} text - Codemiror text value
174 * @param {string} text - Codemiror text value
174 * @see TextCell#get_text
175 * @see TextCell#get_text
175 * @method set_text
176 * @method set_text
176 * */
177 * */
177 TextCell.prototype.set_text = function(text) {
178 TextCell.prototype.set_text = function(text) {
178 this.code_mirror.setValue(text);
179 this.code_mirror.setValue(text);
179 this.code_mirror.refresh();
180 this.code_mirror.refresh();
180 };
181 };
181
182
182 /**
183 /**
183 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
184 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
184 * @method get_rendered
185 * @method get_rendered
185 * @return {html} html of rendered element
186 * @return {html} html of rendered element
186 * */
187 * */
187 TextCell.prototype.get_rendered = function() {
188 TextCell.prototype.get_rendered = function() {
188 return this.element.find('div.text_cell_render').html();
189 return this.element.find('div.text_cell_render').html();
189 };
190 };
190
191
191 /**
192 /**
192 * @method set_rendered
193 * @method set_rendered
193 */
194 */
194 TextCell.prototype.set_rendered = function(text) {
195 TextCell.prototype.set_rendered = function(text) {
195 this.element.find('div.text_cell_render').html(text);
196 this.element.find('div.text_cell_render').html(text);
196 };
197 };
197
198
198 /**
199 /**
199 * not deprecated, but implementation wrong
200 * not deprecated, but implementation wrong
200 * @method at_top
201 * @method at_top
201 * @deprecated
202 * @deprecated
202 * @return {Boolean} true is cell rendered, false otherwise
203 * @return {Boolean} true is cell rendered, false otherwise
203 * I doubt this is what it is supposed to do
204 * I doubt this is what it is supposed to do
204 * this implementation is completly false
205 * this implementation is completly false
205 */
206 */
206 TextCell.prototype.at_top = function () {
207 TextCell.prototype.at_top = function () {
207 if (this.rendered) {
208 if (this.rendered) {
208 return true;
209 return true;
209 } else {
210 } else {
210 return false;
211 return false;
211 }
212 }
212 };
213 };
213
214
214
215
215 /**
216 /**
216 * not deprecated, but implementation wrong
217 * not deprecated, but implementation wrong
217 * @method at_bottom
218 * @method at_bottom
218 * @deprecated
219 * @deprecated
219 * @return {Boolean} true is cell rendered, false otherwise
220 * @return {Boolean} true is cell rendered, false otherwise
220 * I doubt this is what it is supposed to do
221 * I doubt this is what it is supposed to do
221 * this implementation is completly false
222 * this implementation is completly false
222 * */
223 * */
223 TextCell.prototype.at_bottom = function () {
224 TextCell.prototype.at_bottom = function () {
224 if (this.rendered) {
225 if (this.rendered) {
225 return true;
226 return true;
226 } else {
227 } else {
227 return false;
228 return false;
228 }
229 }
229 };
230 };
230
231
231 /**
232 /**
232 * Create Text cell from JSON
233 * Create Text cell from JSON
233 * @param {json} data - JSON serialized text-cell
234 * @param {json} data - JSON serialized text-cell
234 * @method fromJSON
235 * @method fromJSON
235 */
236 */
236 TextCell.prototype.fromJSON = function (data) {
237 TextCell.prototype.fromJSON = function (data) {
237 IPython.Cell.prototype.fromJSON.apply(this, arguments);
238 IPython.Cell.prototype.fromJSON.apply(this, arguments);
238 if (data.cell_type === this.cell_type) {
239 if (data.cell_type === this.cell_type) {
239 if (data.source !== undefined) {
240 if (data.source !== undefined) {
240 this.set_text(data.source);
241 this.set_text(data.source);
241 // make this value the starting point, so that we can only undo
242 // make this value the starting point, so that we can only undo
242 // to this state, instead of a blank cell
243 // to this state, instead of a blank cell
243 this.code_mirror.clearHistory();
244 this.code_mirror.clearHistory();
244 this.set_rendered(data.rendered || '');
245 this.set_rendered(data.rendered || '');
245 this.rendered = false;
246 this.rendered = false;
246 this.render();
247 this.render();
247 }
248 }
248 }
249 }
249 };
250 };
250
251
251 /** Generate JSON from cell
252 /** Generate JSON from cell
252 * @return {object} cell data serialised to json
253 * @return {object} cell data serialised to json
253 */
254 */
254 TextCell.prototype.toJSON = function () {
255 TextCell.prototype.toJSON = function () {
255 var data = IPython.Cell.prototype.toJSON.apply(this);
256 var data = IPython.Cell.prototype.toJSON.apply(this);
256 data.cell_type = this.cell_type;
257 data.cell_type = this.cell_type;
257 data.source = this.get_text();
258 data.source = this.get_text();
258 return data;
259 return data;
259 };
260 };
260
261
261
262
262 /**
263 /**
263 * @constructor HtmlCell
264 * @constructor HtmlCell
264 * @class HtmlCell
265 * @class HtmlCell
265 * @extends Ipython.TextCell
266 * @extends Ipython.TextCell
266 */
267 */
267 var HTMLCell = function () {
268 var HTMLCell = function () {
268 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
269 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$";
269 IPython.TextCell.apply(this, arguments);
270 IPython.TextCell.apply(this, arguments);
270 this.cell_type = 'html';
271 this.cell_type = 'html';
271 };
272 };
272
273
273
274
274 HTMLCell.prototype = new TextCell();
275 HTMLCell.prototype = new TextCell();
275
276
276 /**
277 /**
277 * @method render
278 * @method render
278 */
279 */
279 HTMLCell.prototype.render = function () {
280 HTMLCell.prototype.render = function () {
280 if (this.rendered === false) {
281 if (this.rendered === false) {
281 var text = this.get_text();
282 var text = this.get_text();
282 if (text === "") { text = this.placeholder; }
283 if (text === "") { text = this.placeholder; }
283 this.set_rendered(text);
284 this.set_rendered(text);
284 this.typeset();
285 this.typeset();
285 this.element.find('div.text_cell_input').hide();
286 this.element.find('div.text_cell_input').hide();
286 this.element.find("div.text_cell_render").show();
287 this.element.find("div.text_cell_render").show();
287 this.rendered = true;
288 this.rendered = true;
288 }
289 }
289 };
290 };
290
291
291
292
292 /**
293 /**
293 * @class MarkdownCell
294 * @class MarkdownCell
294 * @constructor MarkdownCell
295 * @constructor MarkdownCell
295 * @extends Ipython.HtmlCell
296 * @extends Ipython.HtmlCell
296 */
297 */
297 var MarkdownCell = function () {
298 var MarkdownCell = function () {
298 this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
299 this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$";
299 IPython.TextCell.apply(this, arguments);
300 IPython.TextCell.apply(this, arguments);
300 this.cell_type = 'markdown';
301 this.cell_type = 'markdown';
301 };
302 };
302
303
303
304
304 MarkdownCell.prototype = new TextCell();
305 MarkdownCell.prototype = new TextCell();
305
306
306 /**
307 /**
307 * @method render
308 * @method render
308 */
309 */
309 MarkdownCell.prototype.render = function () {
310 MarkdownCell.prototype.render = function () {
310 if (this.rendered === false) {
311 if (this.rendered === false) {
311 var text = this.get_text();
312 var text = this.get_text();
312 if (text === "") { text = this.placeholder; }
313 if (text === "") { text = this.placeholder; }
313 text = IPython.mathjaxutils.remove_math(text)
314 text = IPython.mathjaxutils.remove_math(text)
314 var html = IPython.markdown_converter.makeHtml(text);
315 var html = IPython.markdown_converter.makeHtml(text);
315 html = IPython.mathjaxutils.replace_math(html)
316 html = IPython.mathjaxutils.replace_math(html)
316 try {
317 try {
317 this.set_rendered(html);
318 this.set_rendered(html);
318 } catch (e) {
319 } catch (e) {
319 console.log("Error running Javascript in Markdown:");
320 console.log("Error running Javascript in Markdown:");
320 console.log(e);
321 console.log(e);
321 this.set_rendered($("<div/>").addClass("js-error").html(
322 this.set_rendered($("<div/>").addClass("js-error").html(
322 "Error rendering Markdown!<br/>" + e.toString())
323 "Error rendering Markdown!<br/>" + e.toString())
323 );
324 );
324 }
325 }
325 this.element.find('div.text_cell_input').hide();
326 this.element.find('div.text_cell_input').hide();
326 this.element.find("div.text_cell_render").show();
327 this.element.find("div.text_cell_render").show();
327 var code_snippets = this.element.find("pre > code");
328 var code_snippets = this.element.find("pre > code");
328 code_snippets.replaceWith(function () {
329 code_snippets.replaceWith(function () {
329 var code = $(this).html();
330 var code = $(this).html();
330 /* Substitute br for newlines and &nbsp; for spaces
331 /* Substitute br for newlines and &nbsp; for spaces
331 before highlighting, since prettify doesn't
332 before highlighting, since prettify doesn't
332 preserve those on all browsers */
333 preserve those on all browsers */
333 code = code.replace(/(\r\n|\n|\r)/gm, "<br/>");
334 code = code.replace(/(\r\n|\n|\r)/gm, "<br/>");
334 code = code.replace(/ /gm, '&nbsp;');
335 code = code.replace(/ /gm, '&nbsp;');
335 code = prettyPrintOne(code);
336 code = prettyPrintOne(code);
336
337
337 return '<code class="prettyprint">' + code + '</code>';
338 return '<code class="prettyprint">' + code + '</code>';
338 });
339 });
339 this.typeset()
340 this.typeset()
340 this.rendered = true;
341 this.rendered = true;
341 }
342 }
342 };
343 };
343
344
344
345
345 // RawCell
346 // RawCell
346
347
347 /**
348 /**
348 * @class RawCell
349 * @class RawCell
349 * @constructor RawCell
350 * @constructor RawCell
350 * @extends Ipython.TextCell
351 * @extends Ipython.TextCell
351 */
352 */
352 var RawCell = function () {
353 var RawCell = function () {
353 this.placeholder = "Type plain text and LaTeX: $\\alpha^2$";
354 this.placeholder = "Type plain text and LaTeX: $\\alpha^2$";
354 this.code_mirror_mode = 'rst';
355 this.code_mirror_mode = 'rst';
355 IPython.TextCell.apply(this, arguments);
356 IPython.TextCell.apply(this, arguments);
356 this.cell_type = 'raw';
357 this.cell_type = 'raw';
357 var that = this
358 var that = this
358
359
359 this.element.focusout(
360 this.element.focusout(
360 function() { that.auto_highlight(); }
361 function() { that.auto_highlight(); }
361 );
362 );
362 };
363 };
363
364
364
365
365 RawCell.prototype = new TextCell();
366 RawCell.prototype = new TextCell();
366
367
367 /**
368 /**
368 * Trigger autodetection of highlight scheme for current cell
369 * Trigger autodetection of highlight scheme for current cell
369 * @method auto_highlight
370 * @method auto_highlight
370 */
371 */
371 RawCell.prototype.auto_highlight = function () {
372 RawCell.prototype.auto_highlight = function () {
372 this._auto_highlight(IPython.config.raw_cell_highlight);
373 this._auto_highlight(IPython.config.raw_cell_highlight);
373 };
374 };
374
375
375 /** @method render **/
376 /** @method render **/
376 RawCell.prototype.render = function () {
377 RawCell.prototype.render = function () {
377 this.rendered = true;
378 this.rendered = true;
378 this.edit();
379 this.edit();
379 };
380 };
380
381
381
382
382 /** @method handle_codemirror_keyevent **/
383 /** @method handle_codemirror_keyevent **/
383 RawCell.prototype.handle_codemirror_keyevent = function (editor, event) {
384 RawCell.prototype.handle_codemirror_keyevent = function (editor, event) {
384
385
385 var that = this;
386 var that = this;
386 if (event.which === key.UPARROW && event.type === 'keydown') {
387 if (event.which === key.UPARROW && event.type === 'keydown') {
387 // If we are not at the top, let CM handle the up arrow and
388 // If we are not at the top, let CM handle the up arrow and
388 // prevent the global keydown handler from handling it.
389 // prevent the global keydown handler from handling it.
389 if (!that.at_top()) {
390 if (!that.at_top()) {
390 event.stop();
391 event.stop();
391 return false;
392 return false;
392 } else {
393 } else {
393 return true;
394 return true;
394 };
395 };
395 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
396 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
396 // If we are not at the bottom, let CM handle the down arrow and
397 // If we are not at the bottom, let CM handle the down arrow and
397 // prevent the global keydown handler from handling it.
398 // prevent the global keydown handler from handling it.
398 if (!that.at_bottom()) {
399 if (!that.at_bottom()) {
399 event.stop();
400 event.stop();
400 return false;
401 return false;
401 } else {
402 } else {
402 return true;
403 return true;
403 };
404 };
404 };
405 };
405 return false;
406 return false;
406 };
407 };
407
408
408 /** @method select **/
409 /** @method select **/
409 RawCell.prototype.select = function () {
410 RawCell.prototype.select = function () {
410 IPython.Cell.prototype.select.apply(this);
411 IPython.Cell.prototype.select.apply(this);
411 this.code_mirror.refresh();
412 this.code_mirror.refresh();
412 this.code_mirror.focus();
413 this.code_mirror.focus();
413 };
414 };
414
415
415 /** @method at_top **/
416 /** @method at_top **/
416 RawCell.prototype.at_top = function () {
417 RawCell.prototype.at_top = function () {
417 var cursor = this.code_mirror.getCursor();
418 var cursor = this.code_mirror.getCursor();
418 if (cursor.line === 0 && cursor.ch === 0) {
419 if (cursor.line === 0 && cursor.ch === 0) {
419 return true;
420 return true;
420 } else {
421 } else {
421 return false;
422 return false;
422 }
423 }
423 };
424 };
424
425
425
426
426 /** @method at_bottom **/
427 /** @method at_bottom **/
427 RawCell.prototype.at_bottom = function () {
428 RawCell.prototype.at_bottom = function () {
428 var cursor = this.code_mirror.getCursor();
429 var cursor = this.code_mirror.getCursor();
429 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
430 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
430 return true;
431 return true;
431 } else {
432 } else {
432 return false;
433 return false;
433 }
434 }
434 };
435 };
435
436
436
437
437 /**
438 /**
438 * @class HeadingCell
439 * @class HeadingCell
439 * @extends Ipython.TextCell
440 * @extends Ipython.TextCell
440 */
441 */
441
442
442 /**
443 /**
443 * @constructor HeadingCell
444 * @constructor HeadingCell
444 * @extends Ipython.TextCell
445 * @extends Ipython.TextCell
445 */
446 */
446 var HeadingCell = function () {
447 var HeadingCell = function () {
447 this.placeholder = "Type Heading Here";
448 this.placeholder = "Type Heading Here";
448 IPython.TextCell.apply(this, arguments);
449 IPython.TextCell.apply(this, arguments);
449 /**
450 /**
450 * heading level of the cell, use getter and setter to access
451 * heading level of the cell, use getter and setter to access
451 * @property level
452 * @property level
452 */
453 */
453 this.level = 1;
454 this.level = 1;
454 this.cell_type = 'heading';
455 this.cell_type = 'heading';
455 };
456 };
456
457
457
458
458 HeadingCell.prototype = new TextCell();
459 HeadingCell.prototype = new TextCell();
459
460
460 /** @method fromJSON */
461 /** @method fromJSON */
461 HeadingCell.prototype.fromJSON = function (data) {
462 HeadingCell.prototype.fromJSON = function (data) {
462 if (data.level != undefined){
463 if (data.level != undefined){
463 this.level = data.level;
464 this.level = data.level;
464 }
465 }
465 IPython.TextCell.prototype.fromJSON.apply(this, arguments);
466 IPython.TextCell.prototype.fromJSON.apply(this, arguments);
466 };
467 };
467
468
468
469
469 /** @method toJSON */
470 /** @method toJSON */
470 HeadingCell.prototype.toJSON = function () {
471 HeadingCell.prototype.toJSON = function () {
471 var data = IPython.TextCell.prototype.toJSON.apply(this);
472 var data = IPython.TextCell.prototype.toJSON.apply(this);
472 data.level = this.get_level();
473 data.level = this.get_level();
473 return data;
474 return data;
474 };
475 };
475
476
476
477
477 /**
478 /**
478 * Change heading level of cell, and re-render
479 * Change heading level of cell, and re-render
479 * @method set_level
480 * @method set_level
480 */
481 */
481 HeadingCell.prototype.set_level = function (level) {
482 HeadingCell.prototype.set_level = function (level) {
482 this.level = level;
483 this.level = level;
483 if (this.rendered) {
484 if (this.rendered) {
484 this.rendered = false;
485 this.rendered = false;
485 this.render();
486 this.render();
486 };
487 };
487 };
488 };
488
489
489 /** The depth of header cell, based on html (h1 to h6)
490 /** The depth of header cell, based on html (h1 to h6)
490 * @method get_level
491 * @method get_level
491 * @return {integer} level - for 1 to 6
492 * @return {integer} level - for 1 to 6
492 */
493 */
493 HeadingCell.prototype.get_level = function () {
494 HeadingCell.prototype.get_level = function () {
494 return this.level;
495 return this.level;
495 };
496 };
496
497
497
498
498 HeadingCell.prototype.set_rendered = function (text) {
499 HeadingCell.prototype.set_rendered = function (text) {
499 var r = this.element.find("div.text_cell_render");
500 var r = this.element.find("div.text_cell_render");
500 r.empty();
501 r.empty();
501 r.append($('<h'+this.level+'/>').html(text));
502 r.append($('<h'+this.level+'/>').html(text));
502 };
503 };
503
504
504
505
505 HeadingCell.prototype.get_rendered = function () {
506 HeadingCell.prototype.get_rendered = function () {
506 var r = this.element.find("div.text_cell_render");
507 var r = this.element.find("div.text_cell_render");
507 return r.children().first().html();
508 return r.children().first().html();
508 };
509 };
509
510
510
511
511 HeadingCell.prototype.render = function () {
512 HeadingCell.prototype.render = function () {
512 if (this.rendered === false) {
513 if (this.rendered === false) {
513 var text = this.get_text();
514 var text = this.get_text();
514 if (text === "") { text = this.placeholder; }
515 if (text === "") { text = this.placeholder; }
515 this.set_rendered(text);
516 this.set_rendered(text);
516 this.typeset();
517 this.typeset();
517 this.element.find('div.text_cell_input').hide();
518 this.element.find('div.text_cell_input').hide();
518 this.element.find("div.text_cell_render").show();
519 this.element.find("div.text_cell_render").show();
519 this.rendered = true;
520 this.rendered = true;
520 };
521 };
521 };
522 };
522
523
523 IPython.TextCell = TextCell;
524 IPython.TextCell = TextCell;
524 IPython.HTMLCell = HTMLCell;
525 IPython.HTMLCell = HTMLCell;
525 IPython.MarkdownCell = MarkdownCell;
526 IPython.MarkdownCell = MarkdownCell;
526 IPython.RawCell = RawCell;
527 IPython.RawCell = RawCell;
527 IPython.HeadingCell = HeadingCell;
528 IPython.HeadingCell = HeadingCell;
528
529
529
530
530 return IPython;
531 return IPython;
531
532
532 }(IPython));
533 }(IPython));
533
534
General Comments 0
You need to be logged in to leave comments. Login now