##// END OF EJS Templates
Always refresh the CM editor upon TextCell unrender.
Brian E. Granger -
Show More
@@ -1,565 +1,564
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
13
14 /**
14 /**
15 A module that allow to create different type of Text Cell
15 A module that allow to create different type of Text Cell
16 @module IPython
16 @module IPython
17 @namespace IPython
17 @namespace IPython
18 */
18 */
19 var IPython = (function (IPython) {
19 var IPython = (function (IPython) {
20 "use strict";
20 "use strict";
21
21
22 // TextCell base class
22 // TextCell base class
23 var key = IPython.utils.keycodes;
23 var key = IPython.utils.keycodes;
24
24
25 /**
25 /**
26 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
26 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
27 * cell start as not redered.
27 * cell start as not redered.
28 *
28 *
29 * @class TextCell
29 * @class TextCell
30 * @constructor TextCell
30 * @constructor TextCell
31 * @extend IPython.Cell
31 * @extend IPython.Cell
32 * @param {object|undefined} [options]
32 * @param {object|undefined} [options]
33 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config
33 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config
34 * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass)
34 * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass)
35 */
35 */
36 var TextCell = function (options) {
36 var TextCell = function (options) {
37 // in all TextCell/Cell subclasses
37 // in all TextCell/Cell subclasses
38 // do not assign most of members here, just pass it down
38 // do not assign most of members here, just pass it down
39 // in the options dict potentially overwriting what you wish.
39 // in the options dict potentially overwriting what you wish.
40 // they will be assigned in the base class.
40 // they will be assigned in the base class.
41
41
42 // we cannot put this as a class key as it has handle to "this".
42 // we cannot put this as a class key as it has handle to "this".
43 var cm_overwrite_options = {
43 var cm_overwrite_options = {
44 onKeyEvent: $.proxy(this.handle_keyevent,this)
44 onKeyEvent: $.proxy(this.handle_keyevent,this)
45 };
45 };
46
46
47 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
47 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
48
48
49 this.cell_type = this.cell_type || 'text';
49 this.cell_type = this.cell_type || 'text';
50
50
51 IPython.Cell.apply(this, [options]);
51 IPython.Cell.apply(this, [options]);
52
52
53 this.rendered = false;
53 this.rendered = false;
54 };
54 };
55
55
56 TextCell.prototype = new IPython.Cell();
56 TextCell.prototype = new IPython.Cell();
57
57
58 TextCell.options_default = {
58 TextCell.options_default = {
59 cm_config : {
59 cm_config : {
60 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
60 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
61 mode: 'htmlmixed',
61 mode: 'htmlmixed',
62 lineWrapping : true,
62 lineWrapping : true,
63 }
63 }
64 };
64 };
65
65
66
66
67 /**
67 /**
68 * Create the DOM element of the TextCell
68 * Create the DOM element of the TextCell
69 * @method create_element
69 * @method create_element
70 * @private
70 * @private
71 */
71 */
72 TextCell.prototype.create_element = function () {
72 TextCell.prototype.create_element = function () {
73 IPython.Cell.prototype.create_element.apply(this, arguments);
73 IPython.Cell.prototype.create_element.apply(this, arguments);
74
74
75 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
75 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
76 cell.attr('tabindex','2');
76 cell.attr('tabindex','2');
77
77
78 var prompt = $('<div/>').addClass('prompt input_prompt');
78 var prompt = $('<div/>').addClass('prompt input_prompt');
79 cell.append(prompt);
79 cell.append(prompt);
80 var inner_cell = $('<div/>').addClass('inner_cell');
80 var inner_cell = $('<div/>').addClass('inner_cell');
81 this.celltoolbar = new IPython.CellToolbar(this);
81 this.celltoolbar = new IPython.CellToolbar(this);
82 inner_cell.append(this.celltoolbar.element);
82 inner_cell.append(this.celltoolbar.element);
83 var input_area = $('<div/>').addClass('text_cell_input border-box-sizing');
83 var input_area = $('<div/>').addClass('text_cell_input border-box-sizing');
84 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
84 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
85 // The tabindex=-1 makes this div focusable.
85 // The tabindex=-1 makes this div focusable.
86 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
86 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
87 addClass('rendered_html').attr('tabindex','-1');
87 addClass('rendered_html').attr('tabindex','-1');
88 inner_cell.append(input_area).append(render_area);
88 inner_cell.append(input_area).append(render_area);
89 cell.append(inner_cell);
89 cell.append(inner_cell);
90 this.element = cell;
90 this.element = cell;
91 };
91 };
92
92
93
93
94 /**
94 /**
95 * Bind the DOM evet to cell actions
95 * Bind the DOM evet to cell actions
96 * Need to be called after TextCell.create_element
96 * Need to be called after TextCell.create_element
97 * @private
97 * @private
98 * @method bind_event
98 * @method bind_event
99 */
99 */
100 TextCell.prototype.bind_events = function () {
100 TextCell.prototype.bind_events = function () {
101 IPython.Cell.prototype.bind_events.apply(this);
101 IPython.Cell.prototype.bind_events.apply(this);
102 var that = this;
102 var that = this;
103
103
104 this.element.dblclick(function () {
104 this.element.dblclick(function () {
105 if (that.selected === false) {
105 if (that.selected === false) {
106 $([IPython.events]).trigger('select.Cell', {'cell':that});
106 $([IPython.events]).trigger('select.Cell', {'cell':that});
107 }
107 }
108 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
108 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
109 });
109 });
110 };
110 };
111
111
112 TextCell.prototype.handle_keyevent = function (editor, event) {
112 TextCell.prototype.handle_keyevent = function (editor, event) {
113
113
114 // console.log('CM', this.mode, event.which, event.type)
114 // console.log('CM', this.mode, event.which, event.type)
115
115
116 if (this.mode === 'command') {
116 if (this.mode === 'command') {
117 return true;
117 return true;
118 } else if (this.mode === 'edit') {
118 } else if (this.mode === 'edit') {
119 return this.handle_codemirror_keyevent(editor, event);
119 return this.handle_codemirror_keyevent(editor, event);
120 }
120 }
121 };
121 };
122
122
123 /**
123 /**
124 * This method gets called in CodeMirror's onKeyDown/onKeyPress
124 * This method gets called in CodeMirror's onKeyDown/onKeyPress
125 * handlers and is used to provide custom key handling.
125 * handlers and is used to provide custom key handling.
126 *
126 *
127 * Subclass should override this method to have custom handeling
127 * Subclass should override this method to have custom handeling
128 *
128 *
129 * @method handle_codemirror_keyevent
129 * @method handle_codemirror_keyevent
130 * @param {CodeMirror} editor - The codemirror instance bound to the cell
130 * @param {CodeMirror} editor - The codemirror instance bound to the cell
131 * @param {event} event -
131 * @param {event} event -
132 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
132 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
133 */
133 */
134 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
134 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
135 var that = this;
135 var that = this;
136
136
137 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) {
137 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) {
138 // Always ignore shift-enter in CodeMirror as we handle it.
138 // Always ignore shift-enter in CodeMirror as we handle it.
139 return true;
139 return true;
140 } else if (event.which === key.UPARROW && event.type === 'keydown') {
140 } else if (event.which === key.UPARROW && event.type === 'keydown') {
141 // If we are not at the top, let CM handle the up arrow and
141 // If we are not at the top, let CM handle the up arrow and
142 // prevent the global keydown handler from handling it.
142 // prevent the global keydown handler from handling it.
143 if (!that.at_top()) {
143 if (!that.at_top()) {
144 event.stop();
144 event.stop();
145 return false;
145 return false;
146 } else {
146 } else {
147 return true;
147 return true;
148 }
148 }
149 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
149 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
150 // If we are not at the bottom, let CM handle the down arrow and
150 // If we are not at the bottom, let CM handle the down arrow and
151 // prevent the global keydown handler from handling it.
151 // prevent the global keydown handler from handling it.
152 if (!that.at_bottom()) {
152 if (!that.at_bottom()) {
153 event.stop();
153 event.stop();
154 return false;
154 return false;
155 } else {
155 } else {
156 return true;
156 return true;
157 }
157 }
158 } else if (event.which === key.ESC && event.type === 'keydown') {
158 } else if (event.which === key.ESC && event.type === 'keydown') {
159 if (that.code_mirror.options.keyMap === "vim-insert") {
159 if (that.code_mirror.options.keyMap === "vim-insert") {
160 // vim keyMap is active and in insert mode. In this case we leave vim
160 // vim keyMap is active and in insert mode. In this case we leave vim
161 // insert mode, but remain in notebook edit mode.
161 // insert mode, but remain in notebook edit mode.
162 // Let' CM handle this event and prevent global handling.
162 // Let' CM handle this event and prevent global handling.
163 event.stop();
163 event.stop();
164 return false;
164 return false;
165 } else {
165 } else {
166 // vim keyMap is not active. Leave notebook edit mode.
166 // vim keyMap is not active. Leave notebook edit mode.
167 // Don't let CM handle the event, defer to global handling.
167 // Don't let CM handle the event, defer to global handling.
168 return true;
168 return true;
169 }
169 }
170 }
170 }
171 return false;
171 return false;
172 };
172 };
173
173
174 // Cell level actions
174 // Cell level actions
175
175
176 TextCell.prototype.select = function () {
176 TextCell.prototype.select = function () {
177 var cont = IPython.Cell.prototype.select.apply(this);
177 var cont = IPython.Cell.prototype.select.apply(this);
178 if (cont) {
178 if (cont) {
179 if (this.mode === 'edit') {
179 if (this.mode === 'edit') {
180 this.code_mirror.refresh();
180 this.code_mirror.refresh();
181 }
181 }
182 }
182 }
183 return cont;
183 return cont;
184 };
184 };
185
185
186 TextCell.prototype.unrender = function () {
186 TextCell.prototype.unrender = function () {
187 if (this.read_only) return;
187 if (this.read_only) return;
188 var cont = IPython.Cell.prototype.unrender.apply(this);
188 var cont = IPython.Cell.prototype.unrender.apply(this);
189 if (cont) {
189 if (cont) {
190 var text_cell = this.element;
190 var text_cell = this.element;
191 var output = text_cell.find("div.text_cell_render");
191 var output = text_cell.find("div.text_cell_render");
192 output.hide();
192 output.hide();
193 text_cell.find('div.text_cell_input').show();
193 text_cell.find('div.text_cell_input').show();
194 if (this.get_text() === this.placeholder) {
194 if (this.get_text() === this.placeholder) {
195 this.set_text('');
195 this.set_text('');
196 this.refresh();
197 }
196 }
198
197 this.refresh();
199 }
198 }
200 return cont;
199 return cont;
201 };
200 };
202
201
203 TextCell.prototype.execute = function () {
202 TextCell.prototype.execute = function () {
204 this.render();
203 this.render();
205 };
204 };
206
205
207 TextCell.prototype.edit_mode = function (focus_editor) {
206 TextCell.prototype.edit_mode = function (focus_editor) {
208 var cont = IPython.Cell.prototype.edit_mode.apply(this);
207 var cont = IPython.Cell.prototype.edit_mode.apply(this);
209 if (cont) {
208 if (cont) {
210 cont = this.unrender();
209 cont = this.unrender();
211 // Focus the editor if codemirror was just added to the page or the
210 // Focus the editor if codemirror was just added to the page or the
212 // caller explicitly wants to focus the editor (usally when the
211 // caller explicitly wants to focus the editor (usally when the
213 // edit_mode was triggered by something other than a mouse click).
212 // edit_mode was triggered by something other than a mouse click).
214 if (cont || focus_editor) {
213 if (cont || focus_editor) {
215 this.focus_editor();
214 this.focus_editor();
216 }
215 }
217 }
216 }
218 return cont;
217 return cont;
219 };
218 };
220
219
221 /**
220 /**
222 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
221 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
223 * @method get_text
222 * @method get_text
224 * @retrun {string} CodeMirror current text value
223 * @retrun {string} CodeMirror current text value
225 */
224 */
226 TextCell.prototype.get_text = function() {
225 TextCell.prototype.get_text = function() {
227 return this.code_mirror.getValue();
226 return this.code_mirror.getValue();
228 };
227 };
229
228
230 /**
229 /**
231 * @param {string} text - Codemiror text value
230 * @param {string} text - Codemiror text value
232 * @see TextCell#get_text
231 * @see TextCell#get_text
233 * @method set_text
232 * @method set_text
234 * */
233 * */
235 TextCell.prototype.set_text = function(text) {
234 TextCell.prototype.set_text = function(text) {
236 this.code_mirror.setValue(text);
235 this.code_mirror.setValue(text);
237 this.code_mirror.refresh();
236 this.code_mirror.refresh();
238 };
237 };
239
238
240 /**
239 /**
241 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
240 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
242 * @method get_rendered
241 * @method get_rendered
243 * @return {html} html of rendered element
242 * @return {html} html of rendered element
244 * */
243 * */
245 TextCell.prototype.get_rendered = function() {
244 TextCell.prototype.get_rendered = function() {
246 return this.element.find('div.text_cell_render').html();
245 return this.element.find('div.text_cell_render').html();
247 };
246 };
248
247
249 /**
248 /**
250 * @method set_rendered
249 * @method set_rendered
251 */
250 */
252 TextCell.prototype.set_rendered = function(text) {
251 TextCell.prototype.set_rendered = function(text) {
253 this.element.find('div.text_cell_render').html(text);
252 this.element.find('div.text_cell_render').html(text);
254 };
253 };
255
254
256 /**
255 /**
257 * @method at_top
256 * @method at_top
258 * @return {Boolean}
257 * @return {Boolean}
259 */
258 */
260 TextCell.prototype.at_top = function () {
259 TextCell.prototype.at_top = function () {
261 if (this.rendered) {
260 if (this.rendered) {
262 return true;
261 return true;
263 } else {
262 } else {
264 var cursor = this.code_mirror.getCursor();
263 var cursor = this.code_mirror.getCursor();
265 if (cursor.line === 0 && cursor.ch === 0) {
264 if (cursor.line === 0 && cursor.ch === 0) {
266 return true;
265 return true;
267 } else {
266 } else {
268 return false;
267 return false;
269 }
268 }
270 }
269 }
271 };
270 };
272
271
273 /**
272 /**
274 * @method at_bottom
273 * @method at_bottom
275 * @return {Boolean}
274 * @return {Boolean}
276 * */
275 * */
277 TextCell.prototype.at_bottom = function () {
276 TextCell.prototype.at_bottom = function () {
278 if (this.rendered) {
277 if (this.rendered) {
279 return true;
278 return true;
280 } else {
279 } else {
281 var cursor = this.code_mirror.getCursor();
280 var cursor = this.code_mirror.getCursor();
282 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
281 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
283 return true;
282 return true;
284 } else {
283 } else {
285 return false;
284 return false;
286 }
285 }
287 }
286 }
288 };
287 };
289
288
290 /**
289 /**
291 * Create Text cell from JSON
290 * Create Text cell from JSON
292 * @param {json} data - JSON serialized text-cell
291 * @param {json} data - JSON serialized text-cell
293 * @method fromJSON
292 * @method fromJSON
294 */
293 */
295 TextCell.prototype.fromJSON = function (data) {
294 TextCell.prototype.fromJSON = function (data) {
296 IPython.Cell.prototype.fromJSON.apply(this, arguments);
295 IPython.Cell.prototype.fromJSON.apply(this, arguments);
297 if (data.cell_type === this.cell_type) {
296 if (data.cell_type === this.cell_type) {
298 if (data.source !== undefined) {
297 if (data.source !== undefined) {
299 this.set_text(data.source);
298 this.set_text(data.source);
300 // make this value the starting point, so that we can only undo
299 // make this value the starting point, so that we can only undo
301 // to this state, instead of a blank cell
300 // to this state, instead of a blank cell
302 this.code_mirror.clearHistory();
301 this.code_mirror.clearHistory();
303 this.set_rendered(data.rendered || '');
302 this.set_rendered(data.rendered || '');
304 this.rendered = false;
303 this.rendered = false;
305 this.render();
304 this.render();
306 }
305 }
307 }
306 }
308 };
307 };
309
308
310 /** Generate JSON from cell
309 /** Generate JSON from cell
311 * @return {object} cell data serialised to json
310 * @return {object} cell data serialised to json
312 */
311 */
313 TextCell.prototype.toJSON = function () {
312 TextCell.prototype.toJSON = function () {
314 var data = IPython.Cell.prototype.toJSON.apply(this);
313 var data = IPython.Cell.prototype.toJSON.apply(this);
315 data.source = this.get_text();
314 data.source = this.get_text();
316 if (data.source == this.placeholder) {
315 if (data.source == this.placeholder) {
317 data.source = "";
316 data.source = "";
318 }
317 }
319 return data;
318 return data;
320 };
319 };
321
320
322
321
323 /**
322 /**
324 * @class MarkdownCell
323 * @class MarkdownCell
325 * @constructor MarkdownCell
324 * @constructor MarkdownCell
326 * @extends IPython.HTMLCell
325 * @extends IPython.HTMLCell
327 */
326 */
328 var MarkdownCell = function (options) {
327 var MarkdownCell = function (options) {
329 options = this.mergeopt(MarkdownCell, options);
328 options = this.mergeopt(MarkdownCell, options);
330
329
331 this.cell_type = 'markdown';
330 this.cell_type = 'markdown';
332 TextCell.apply(this, [options]);
331 TextCell.apply(this, [options]);
333 };
332 };
334
333
335 MarkdownCell.options_default = {
334 MarkdownCell.options_default = {
336 cm_config: {
335 cm_config: {
337 mode: 'gfm'
336 mode: 'gfm'
338 },
337 },
339 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
338 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
340 };
339 };
341
340
342 MarkdownCell.prototype = new TextCell();
341 MarkdownCell.prototype = new TextCell();
343
342
344 /**
343 /**
345 * @method render
344 * @method render
346 */
345 */
347 MarkdownCell.prototype.render = function () {
346 MarkdownCell.prototype.render = function () {
348 var cont = IPython.TextCell.prototype.render.apply(this);
347 var cont = IPython.TextCell.prototype.render.apply(this);
349 if (cont) {
348 if (cont) {
350 var text = this.get_text();
349 var text = this.get_text();
351 var math = null;
350 var math = null;
352 if (text === "") { text = this.placeholder; }
351 if (text === "") { text = this.placeholder; }
353 var text_and_math = IPython.mathjaxutils.remove_math(text);
352 var text_and_math = IPython.mathjaxutils.remove_math(text);
354 text = text_and_math[0];
353 text = text_and_math[0];
355 math = text_and_math[1];
354 math = text_and_math[1];
356 var html = marked.parser(marked.lexer(text));
355 var html = marked.parser(marked.lexer(text));
357 html = $(IPython.mathjaxutils.replace_math(html, math));
356 html = $(IPython.mathjaxutils.replace_math(html, math));
358 // links in markdown cells should open in new tabs
357 // links in markdown cells should open in new tabs
359 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
358 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
360 try {
359 try {
361 this.set_rendered(html);
360 this.set_rendered(html);
362 } catch (e) {
361 } catch (e) {
363 console.log("Error running Javascript in Markdown:");
362 console.log("Error running Javascript in Markdown:");
364 console.log(e);
363 console.log(e);
365 this.set_rendered($("<div/>").addClass("js-error").html(
364 this.set_rendered($("<div/>").addClass("js-error").html(
366 "Error rendering Markdown!<br/>" + e.toString())
365 "Error rendering Markdown!<br/>" + e.toString())
367 );
366 );
368 }
367 }
369 this.element.find('div.text_cell_input').hide();
368 this.element.find('div.text_cell_input').hide();
370 this.element.find("div.text_cell_render").show();
369 this.element.find("div.text_cell_render").show();
371 this.typeset();
370 this.typeset();
372 }
371 }
373 return cont;
372 return cont;
374 };
373 };
375
374
376
375
377 // RawCell
376 // RawCell
378
377
379 /**
378 /**
380 * @class RawCell
379 * @class RawCell
381 * @constructor RawCell
380 * @constructor RawCell
382 * @extends IPython.TextCell
381 * @extends IPython.TextCell
383 */
382 */
384 var RawCell = function (options) {
383 var RawCell = function (options) {
385
384
386 options = this.mergeopt(RawCell,options);
385 options = this.mergeopt(RawCell,options);
387 TextCell.apply(this, [options]);
386 TextCell.apply(this, [options]);
388 this.cell_type = 'raw';
387 this.cell_type = 'raw';
389 // RawCell should always hide its rendered div
388 // RawCell should always hide its rendered div
390 this.element.find('div.text_cell_render').hide();
389 this.element.find('div.text_cell_render').hide();
391 };
390 };
392
391
393 RawCell.options_default = {
392 RawCell.options_default = {
394 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert.\n" +
393 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert.\n" +
395 "It will not be rendered in the notebook.\n" +
394 "It will not be rendered in the notebook.\n" +
396 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
395 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
397 };
396 };
398
397
399 RawCell.prototype = new TextCell();
398 RawCell.prototype = new TextCell();
400
399
401 /** @method bind_events **/
400 /** @method bind_events **/
402 RawCell.prototype.bind_events = function () {
401 RawCell.prototype.bind_events = function () {
403 TextCell.prototype.bind_events.apply(this);
402 TextCell.prototype.bind_events.apply(this);
404 var that = this;
403 var that = this;
405 this.element.focusout(function() {
404 this.element.focusout(function() {
406 that.auto_highlight();
405 that.auto_highlight();
407 });
406 });
408 };
407 };
409
408
410 /**
409 /**
411 * Trigger autodetection of highlight scheme for current cell
410 * Trigger autodetection of highlight scheme for current cell
412 * @method auto_highlight
411 * @method auto_highlight
413 */
412 */
414 RawCell.prototype.auto_highlight = function () {
413 RawCell.prototype.auto_highlight = function () {
415 this._auto_highlight(IPython.config.raw_cell_highlight);
414 this._auto_highlight(IPython.config.raw_cell_highlight);
416 };
415 };
417
416
418 /** @method render **/
417 /** @method render **/
419 RawCell.prototype.render = function () {
418 RawCell.prototype.render = function () {
420 // Make sure that this cell type can never be rendered
419 // Make sure that this cell type can never be rendered
421 if (this.rendered) {
420 if (this.rendered) {
422 this.unrender();
421 this.unrender();
423 }
422 }
424 var text = this.get_text();
423 var text = this.get_text();
425 if (text === "") { text = this.placeholder; }
424 if (text === "") { text = this.placeholder; }
426 this.set_text(text);
425 this.set_text(text);
427 };
426 };
428
427
429
428
430 /**
429 /**
431 * @class HeadingCell
430 * @class HeadingCell
432 * @extends IPython.TextCell
431 * @extends IPython.TextCell
433 */
432 */
434
433
435 /**
434 /**
436 * @constructor HeadingCell
435 * @constructor HeadingCell
437 * @extends IPython.TextCell
436 * @extends IPython.TextCell
438 */
437 */
439 var HeadingCell = function (options) {
438 var HeadingCell = function (options) {
440 options = this.mergeopt(HeadingCell, options);
439 options = this.mergeopt(HeadingCell, options);
441
440
442 this.level = 1;
441 this.level = 1;
443 this.cell_type = 'heading';
442 this.cell_type = 'heading';
444 TextCell.apply(this, [options]);
443 TextCell.apply(this, [options]);
445
444
446 /**
445 /**
447 * heading level of the cell, use getter and setter to access
446 * heading level of the cell, use getter and setter to access
448 * @property level
447 * @property level
449 */
448 */
450 };
449 };
451
450
452 HeadingCell.options_default = {
451 HeadingCell.options_default = {
453 placeholder: "Type Heading Here"
452 placeholder: "Type Heading Here"
454 };
453 };
455
454
456 HeadingCell.prototype = new TextCell();
455 HeadingCell.prototype = new TextCell();
457
456
458 /** @method fromJSON */
457 /** @method fromJSON */
459 HeadingCell.prototype.fromJSON = function (data) {
458 HeadingCell.prototype.fromJSON = function (data) {
460 if (data.level !== undefined){
459 if (data.level !== undefined){
461 this.level = data.level;
460 this.level = data.level;
462 }
461 }
463 TextCell.prototype.fromJSON.apply(this, arguments);
462 TextCell.prototype.fromJSON.apply(this, arguments);
464 };
463 };
465
464
466
465
467 /** @method toJSON */
466 /** @method toJSON */
468 HeadingCell.prototype.toJSON = function () {
467 HeadingCell.prototype.toJSON = function () {
469 var data = TextCell.prototype.toJSON.apply(this);
468 var data = TextCell.prototype.toJSON.apply(this);
470 data.level = this.get_level();
469 data.level = this.get_level();
471 return data;
470 return data;
472 };
471 };
473
472
474 /**
473 /**
475 * can the cell be split into two cells
474 * can the cell be split into two cells
476 * @method is_splittable
475 * @method is_splittable
477 **/
476 **/
478 HeadingCell.prototype.is_splittable = function () {
477 HeadingCell.prototype.is_splittable = function () {
479 return false;
478 return false;
480 };
479 };
481
480
482
481
483 /**
482 /**
484 * can the cell be merged with other cells
483 * can the cell be merged with other cells
485 * @method is_mergeable
484 * @method is_mergeable
486 **/
485 **/
487 HeadingCell.prototype.is_mergeable = function () {
486 HeadingCell.prototype.is_mergeable = function () {
488 return false;
487 return false;
489 };
488 };
490
489
491 /**
490 /**
492 * Change heading level of cell, and re-render
491 * Change heading level of cell, and re-render
493 * @method set_level
492 * @method set_level
494 */
493 */
495 HeadingCell.prototype.set_level = function (level) {
494 HeadingCell.prototype.set_level = function (level) {
496 this.level = level;
495 this.level = level;
497 if (this.rendered) {
496 if (this.rendered) {
498 this.rendered = false;
497 this.rendered = false;
499 this.render();
498 this.render();
500 }
499 }
501 };
500 };
502
501
503 /** The depth of header cell, based on html (h1 to h6)
502 /** The depth of header cell, based on html (h1 to h6)
504 * @method get_level
503 * @method get_level
505 * @return {integer} level - for 1 to 6
504 * @return {integer} level - for 1 to 6
506 */
505 */
507 HeadingCell.prototype.get_level = function () {
506 HeadingCell.prototype.get_level = function () {
508 return this.level;
507 return this.level;
509 };
508 };
510
509
511
510
512 HeadingCell.prototype.set_rendered = function (html) {
511 HeadingCell.prototype.set_rendered = function (html) {
513 this.element.find("div.text_cell_render").html(html);
512 this.element.find("div.text_cell_render").html(html);
514 };
513 };
515
514
516
515
517 HeadingCell.prototype.get_rendered = function () {
516 HeadingCell.prototype.get_rendered = function () {
518 var r = this.element.find("div.text_cell_render");
517 var r = this.element.find("div.text_cell_render");
519 return r.children().first().html();
518 return r.children().first().html();
520 };
519 };
521
520
522
521
523 HeadingCell.prototype.render = function () {
522 HeadingCell.prototype.render = function () {
524 var cont = IPython.TextCell.prototype.render.apply(this);
523 var cont = IPython.TextCell.prototype.render.apply(this);
525 if (cont) {
524 if (cont) {
526 var text = this.get_text();
525 var text = this.get_text();
527 var math = null;
526 var math = null;
528 // Markdown headings must be a single line
527 // Markdown headings must be a single line
529 text = text.replace(/\n/g, ' ');
528 text = text.replace(/\n/g, ' ');
530 if (text === "") { text = this.placeholder; }
529 if (text === "") { text = this.placeholder; }
531 text = Array(this.level + 1).join("#") + " " + text;
530 text = Array(this.level + 1).join("#") + " " + text;
532 var text_and_math = IPython.mathjaxutils.remove_math(text);
531 var text_and_math = IPython.mathjaxutils.remove_math(text);
533 text = text_and_math[0];
532 text = text_and_math[0];
534 math = text_and_math[1];
533 math = text_and_math[1];
535 var html = marked.parser(marked.lexer(text));
534 var html = marked.parser(marked.lexer(text));
536 var h = $(IPython.mathjaxutils.replace_math(html, math));
535 var h = $(IPython.mathjaxutils.replace_math(html, math));
537 // add id and linkback anchor
536 // add id and linkback anchor
538 var hash = h.text().replace(/ /g, '-');
537 var hash = h.text().replace(/ /g, '-');
539 h.attr('id', hash);
538 h.attr('id', hash);
540 h.append(
539 h.append(
541 $('<a/>')
540 $('<a/>')
542 .addClass('anchor-link')
541 .addClass('anchor-link')
543 .attr('href', '#' + hash)
542 .attr('href', '#' + hash)
544 .text('¶')
543 .text('¶')
545 );
544 );
546
545
547 this.set_rendered(h);
546 this.set_rendered(h);
548 this.typeset();
547 this.typeset();
549 this.element.find('div.text_cell_input').hide();
548 this.element.find('div.text_cell_input').hide();
550 this.element.find("div.text_cell_render").show();
549 this.element.find("div.text_cell_render").show();
551
550
552 }
551 }
553 return cont;
552 return cont;
554 };
553 };
555
554
556 IPython.TextCell = TextCell;
555 IPython.TextCell = TextCell;
557 IPython.MarkdownCell = MarkdownCell;
556 IPython.MarkdownCell = MarkdownCell;
558 IPython.RawCell = RawCell;
557 IPython.RawCell = RawCell;
559 IPython.HeadingCell = HeadingCell;
558 IPython.HeadingCell = HeadingCell;
560
559
561
560
562 return IPython;
561 return IPython;
563
562
564 }(IPython));
563 }(IPython));
565
564
General Comments 0
You need to be logged in to leave comments. Login now