##// END OF EJS Templates
Move todos into set_rendered
Jonathan Frederic -
Show More
@@ -1,562 +1,564 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
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 = CodeMirror(input_area.get(0), this.cm_config);
84 this.code_mirror = 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();
196 this.refresh();
197 }
197 }
198
198
199 };
199 };
200 return cont;
200 return cont;
201 };
201 };
202
202
203 TextCell.prototype.execute = function () {
203 TextCell.prototype.execute = function () {
204 this.render();
204 this.render();
205 };
205 };
206
206
207 TextCell.prototype.edit_mode = function () {
207 TextCell.prototype.edit_mode = function () {
208 var cont = IPython.Cell.prototype.edit_mode.apply(this);
208 var cont = IPython.Cell.prototype.edit_mode.apply(this);
209 if (cont) {
209 if (cont) {
210 this.unrender();
210 this.unrender();
211 this.focus_editor();
211 this.focus_editor();
212 };
212 };
213 return cont;
213 return cont;
214 }
214 }
215
215
216 /**
216 /**
217 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
217 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
218 * @method get_text
218 * @method get_text
219 * @retrun {string} CodeMirror current text value
219 * @retrun {string} CodeMirror current text value
220 */
220 */
221 TextCell.prototype.get_text = function() {
221 TextCell.prototype.get_text = function() {
222 return this.code_mirror.getValue();
222 return this.code_mirror.getValue();
223 };
223 };
224
224
225 /**
225 /**
226 * @param {string} text - Codemiror text value
226 * @param {string} text - Codemiror text value
227 * @see TextCell#get_text
227 * @see TextCell#get_text
228 * @method set_text
228 * @method set_text
229 * */
229 * */
230 TextCell.prototype.set_text = function(text) {
230 TextCell.prototype.set_text = function(text) {
231 this.code_mirror.setValue(text);
231 this.code_mirror.setValue(text);
232 this.code_mirror.refresh();
232 this.code_mirror.refresh();
233 };
233 };
234
234
235 /**
235 /**
236 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
236 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
237 * @method get_rendered
237 * @method get_rendered
238 * @return {html} html of rendered element
238 * @return {html} html of rendered element
239 * */
239 * */
240 TextCell.prototype.get_rendered = function() {
240 TextCell.prototype.get_rendered = function() {
241 return this.element.find('div.text_cell_render').html();
241 return this.element.find('div.text_cell_render').html();
242 };
242 };
243
243
244 /**
244 /**
245 * @method set_rendered
245 * @method set_rendered
246 */
246 */
247 TextCell.prototype.set_rendered = function(text) {
247 TextCell.prototype.set_rendered = function(text) {
248 this.element.find('div.text_cell_render').text(text);
248 // TODO: This HTML needs to be treated as potentially dangerous
249 // user input.
250 this.element.find('div.text_cell_render').html(text);
249 };
251 };
250
252
251 /**
253 /**
252 * @method at_top
254 * @method at_top
253 * @return {Boolean}
255 * @return {Boolean}
254 */
256 */
255 TextCell.prototype.at_top = function () {
257 TextCell.prototype.at_top = function () {
256 if (this.rendered) {
258 if (this.rendered) {
257 return true;
259 return true;
258 } else {
260 } else {
259 var cursor = this.code_mirror.getCursor();
261 var cursor = this.code_mirror.getCursor();
260 if (cursor.line === 0 && cursor.ch === 0) {
262 if (cursor.line === 0 && cursor.ch === 0) {
261 return true;
263 return true;
262 } else {
264 } else {
263 return false;
265 return false;
264 };
266 };
265 };
267 };
266 };
268 };
267
269
268 /**
270 /**
269 * @method at_bottom
271 * @method at_bottom
270 * @return {Boolean}
272 * @return {Boolean}
271 * */
273 * */
272 TextCell.prototype.at_bottom = function () {
274 TextCell.prototype.at_bottom = function () {
273 if (this.rendered) {
275 if (this.rendered) {
274 return true;
276 return true;
275 } else {
277 } else {
276 var cursor = this.code_mirror.getCursor();
278 var cursor = this.code_mirror.getCursor();
277 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
279 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
278 return true;
280 return true;
279 } else {
281 } else {
280 return false;
282 return false;
281 };
283 };
282 };
284 };
283 };
285 };
284
286
285 /**
287 /**
286 * Create Text cell from JSON
288 * Create Text cell from JSON
287 * @param {json} data - JSON serialized text-cell
289 * @param {json} data - JSON serialized text-cell
288 * @method fromJSON
290 * @method fromJSON
289 */
291 */
290 TextCell.prototype.fromJSON = function (data) {
292 TextCell.prototype.fromJSON = function (data) {
291 IPython.Cell.prototype.fromJSON.apply(this, arguments);
293 IPython.Cell.prototype.fromJSON.apply(this, arguments);
292 if (data.cell_type === this.cell_type) {
294 if (data.cell_type === this.cell_type) {
293 if (data.source !== undefined) {
295 if (data.source !== undefined) {
294 this.set_text(data.source);
296 this.set_text(data.source);
295 // make this value the starting point, so that we can only undo
297 // make this value the starting point, so that we can only undo
296 // to this state, instead of a blank cell
298 // to this state, instead of a blank cell
297 this.code_mirror.clearHistory();
299 this.code_mirror.clearHistory();
298 this.set_rendered(data.rendered || '');
300 this.set_rendered(data.rendered || '');
299 this.rendered = false;
301 this.rendered = false;
300 this.render();
302 this.render();
301 }
303 }
302 }
304 }
303 };
305 };
304
306
305 /** Generate JSON from cell
307 /** Generate JSON from cell
306 * @return {object} cell data serialised to json
308 * @return {object} cell data serialised to json
307 */
309 */
308 TextCell.prototype.toJSON = function () {
310 TextCell.prototype.toJSON = function () {
309 var data = IPython.Cell.prototype.toJSON.apply(this);
311 var data = IPython.Cell.prototype.toJSON.apply(this);
310 data.source = this.get_text();
312 data.source = this.get_text();
311 if (data.source == this.placeholder) {
313 if (data.source == this.placeholder) {
312 data.source = "";
314 data.source = "";
313 }
315 }
314 return data;
316 return data;
315 };
317 };
316
318
317
319
318 /**
320 /**
319 * @class MarkdownCell
321 * @class MarkdownCell
320 * @constructor MarkdownCell
322 * @constructor MarkdownCell
321 * @extends IPython.HTMLCell
323 * @extends IPython.HTMLCell
322 */
324 */
323 var MarkdownCell = function (options) {
325 var MarkdownCell = function (options) {
324 options = this.mergeopt(MarkdownCell, options);
326 options = this.mergeopt(MarkdownCell, options);
325
327
326 this.cell_type = 'markdown';
328 this.cell_type = 'markdown';
327 TextCell.apply(this, [options]);
329 TextCell.apply(this, [options]);
328 };
330 };
329
331
330 MarkdownCell.options_default = {
332 MarkdownCell.options_default = {
331 cm_config: {
333 cm_config: {
332 mode: 'gfm'
334 mode: 'gfm'
333 },
335 },
334 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
336 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
335 }
337 }
336
338
337 MarkdownCell.prototype = new TextCell();
339 MarkdownCell.prototype = new TextCell();
338
340
339 /**
341 /**
340 * @method render
342 * @method render
341 */
343 */
342 MarkdownCell.prototype.render = function () {
344 MarkdownCell.prototype.render = function () {
343 var cont = IPython.TextCell.prototype.render.apply(this);
345 var cont = IPython.TextCell.prototype.render.apply(this);
344 if (cont) {
346 if (cont) {
345 var text = this.get_text();
347 var text = this.get_text();
346 var math = null;
348 var math = null;
347 if (text === "") { text = this.placeholder; }
349 if (text === "") { text = this.placeholder; }
348 var text_and_math = IPython.mathjaxutils.remove_math(text);
350 var text_and_math = IPython.mathjaxutils.remove_math(text);
349 text = text_and_math[0];
351 text = text_and_math[0];
350 math = text_and_math[1];
352 math = text_and_math[1];
351 var html = marked.parser(marked.lexer(text));
353 var html = marked.parser(marked.lexer(text));
352 html = $(IPython.mathjaxutils.replace_math(html, math));
354 html = $(IPython.mathjaxutils.replace_math(html, math));
353 // Links in markdown cells should open in new tabs.
355 // Links in markdown cells should open in new tabs.
354 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
356 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
355 try {
357 try {
356 // TODO: This HTML needs to be treated as potentially dangerous
358 this.set_rendered(html);
357 // user input.
358 rendered.html(html);
359 } catch (e) {
359 } catch (e) {
360 console.log("Error running Javascript in Markdown:");
360 console.log("Error running Javascript in Markdown:");
361 console.log(e);
361 console.log(e);
362 rendered.empty();
362 this.set_rendered($("<div/>").addClass("js-error").html(
363 rendered.append(
363 "Error rendering Markdown!<br/>" + e.toString())
364 $("<div/>")
365 .append($("<div/>").text('Error rendering Markdown!').addClass("js-error"))
366 .append($("<div/>").text(e.toString()).addClass("js-error"))
367 );
364 );
368 }
365 }
369 this.element.find('div.text_cell_input').hide();
366 this.element.find('div.text_cell_input').hide();
370 this.element.find("div.text_cell_render").show();
367 this.element.find("div.text_cell_render").show();
371 this.typeset()
368 this.typeset()
372 };
369 };
373 return cont;
370 return cont;
374 };
371 };
375
372
376
373
377 // RawCell
374 // RawCell
378
375
379 /**
376 /**
380 * @class RawCell
377 * @class RawCell
381 * @constructor RawCell
378 * @constructor RawCell
382 * @extends IPython.TextCell
379 * @extends IPython.TextCell
383 */
380 */
384 var RawCell = function (options) {
381 var RawCell = function (options) {
385
382
386 options = this.mergeopt(RawCell,options)
383 options = this.mergeopt(RawCell,options)
387 TextCell.apply(this, [options]);
384 TextCell.apply(this, [options]);
388 this.cell_type = 'raw';
385 this.cell_type = 'raw';
389 // RawCell should always hide its rendered div
386 // RawCell should always hide its rendered div
390 this.element.find('div.text_cell_render').hide();
387 this.element.find('div.text_cell_render').hide();
391 };
388 };
392
389
393 RawCell.options_default = {
390 RawCell.options_default = {
394 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert.\n" +
391 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert.\n" +
395 "It will not be rendered in the notebook.\n" +
392 "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."
393 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
397 };
394 };
398
395
399 RawCell.prototype = new TextCell();
396 RawCell.prototype = new TextCell();
400
397
401 /** @method bind_events **/
398 /** @method bind_events **/
402 RawCell.prototype.bind_events = function () {
399 RawCell.prototype.bind_events = function () {
403 TextCell.prototype.bind_events.apply(this);
400 TextCell.prototype.bind_events.apply(this);
404 var that = this
401 var that = this
405 this.element.focusout(function() {
402 this.element.focusout(function() {
406 that.auto_highlight();
403 that.auto_highlight();
407 });
404 });
408 };
405 };
409
406
410 /**
407 /**
411 * Trigger autodetection of highlight scheme for current cell
408 * Trigger autodetection of highlight scheme for current cell
412 * @method auto_highlight
409 * @method auto_highlight
413 */
410 */
414 RawCell.prototype.auto_highlight = function () {
411 RawCell.prototype.auto_highlight = function () {
415 this._auto_highlight(IPython.config.raw_cell_highlight);
412 this._auto_highlight(IPython.config.raw_cell_highlight);
416 };
413 };
417
414
418 /** @method render **/
415 /** @method render **/
419 RawCell.prototype.render = function () {
416 RawCell.prototype.render = function () {
420 // Make sure that this cell type can never be rendered
417 // Make sure that this cell type can never be rendered
421 if (this.rendered) {
418 if (this.rendered) {
422 this.unrender();
419 this.unrender();
423 }
420 }
424 var text = this.get_text();
421 var text = this.get_text();
425 if (text === "") { text = this.placeholder; }
422 if (text === "") { text = this.placeholder; }
426 this.set_text(text);
423 this.set_text(text);
427 };
424 };
428
425
429
426
430 /**
427 /**
431 * @class HeadingCell
428 * @class HeadingCell
432 * @extends IPython.TextCell
429 * @extends IPython.TextCell
433 */
430 */
434
431
435 /**
432 /**
436 * @constructor HeadingCell
433 * @constructor HeadingCell
437 * @extends IPython.TextCell
434 * @extends IPython.TextCell
438 */
435 */
439 var HeadingCell = function (options) {
436 var HeadingCell = function (options) {
440 options = this.mergeopt(HeadingCell, options);
437 options = this.mergeopt(HeadingCell, options);
441
438
442 this.level = 1;
439 this.level = 1;
443 this.cell_type = 'heading';
440 this.cell_type = 'heading';
444 TextCell.apply(this, [options]);
441 TextCell.apply(this, [options]);
445
442
446 /**
443 /**
447 * heading level of the cell, use getter and setter to access
444 * heading level of the cell, use getter and setter to access
448 * @property level
445 * @property level
449 */
446 */
450 };
447 };
451
448
452 HeadingCell.options_default = {
449 HeadingCell.options_default = {
453 placeholder: "Type Heading Here"
450 placeholder: "Type Heading Here"
454 };
451 };
455
452
456 HeadingCell.prototype = new TextCell();
453 HeadingCell.prototype = new TextCell();
457
454
458 /** @method fromJSON */
455 /** @method fromJSON */
459 HeadingCell.prototype.fromJSON = function (data) {
456 HeadingCell.prototype.fromJSON = function (data) {
460 if (data.level != undefined){
457 if (data.level != undefined){
461 this.level = data.level;
458 this.level = data.level;
462 }
459 }
463 TextCell.prototype.fromJSON.apply(this, arguments);
460 TextCell.prototype.fromJSON.apply(this, arguments);
464 };
461 };
465
462
466
463
467 /** @method toJSON */
464 /** @method toJSON */
468 HeadingCell.prototype.toJSON = function () {
465 HeadingCell.prototype.toJSON = function () {
469 var data = TextCell.prototype.toJSON.apply(this);
466 var data = TextCell.prototype.toJSON.apply(this);
470 data.level = this.get_level();
467 data.level = this.get_level();
471 return data;
468 return data;
472 };
469 };
473
470
474 /**
471 /**
475 * can the cell be split into two cells
472 * can the cell be split into two cells
476 * @method is_splittable
473 * @method is_splittable
477 **/
474 **/
478 HeadingCell.prototype.is_splittable = function () {
475 HeadingCell.prototype.is_splittable = function () {
479 return false;
476 return false;
480 };
477 };
481
478
482
479
483 /**
480 /**
484 * can the cell be merged with other cells
481 * can the cell be merged with other cells
485 * @method is_mergeable
482 * @method is_mergeable
486 **/
483 **/
487 HeadingCell.prototype.is_mergeable = function () {
484 HeadingCell.prototype.is_mergeable = function () {
488 return false;
485 return false;
489 };
486 };
490
487
491 /**
488 /**
492 * Change heading level of cell, and re-render
489 * Change heading level of cell, and re-render
493 * @method set_level
490 * @method set_level
494 */
491 */
495 HeadingCell.prototype.set_level = function (level) {
492 HeadingCell.prototype.set_level = function (level) {
496 this.level = level;
493 this.level = level;
497 if (this.rendered) {
494 if (this.rendered) {
498 this.rendered = false;
495 this.rendered = false;
499 this.render();
496 this.render();
500 };
497 };
501 };
498 };
502
499
503 /** The depth of header cell, based on html (h1 to h6)
500 /** The depth of header cell, based on html (h1 to h6)
504 * @method get_level
501 * @method get_level
505 * @return {integer} level - for 1 to 6
502 * @return {integer} level - for 1 to 6
506 */
503 */
507 HeadingCell.prototype.get_level = function () {
504 HeadingCell.prototype.get_level = function () {
508 return this.level;
505 return this.level;
509 };
506 };
510
507
511
508
509 HeadingCell.prototype.set_rendered = function (html) {
510 // TODO: This HTML needs to be treated as potentially dangerous
511 // user input.
512 this.element.find("div.text_cell_render").html(html);
513 };
514
515
512 HeadingCell.prototype.get_rendered = function () {
516 HeadingCell.prototype.get_rendered = function () {
513 var r = this.element.find("div.text_cell_render");
517 var r = this.element.find("div.text_cell_render");
514 return r.children().first().html();
518 return r.children().first().html();
515 };
519 };
516
520
517
521
518 HeadingCell.prototype.render = function () {
522 HeadingCell.prototype.render = function () {
519 var cont = IPython.TextCell.prototype.render.apply(this);
523 var cont = IPython.TextCell.prototype.render.apply(this);
520 if (cont) {
524 if (cont) {
521 var text = this.get_text();
525 var text = this.get_text();
522 var math = null;
526 var math = null;
523 // Markdown headings must be a single line
527 // Markdown headings must be a single line
524 text = text.replace(/\n/g, ' ');
528 text = text.replace(/\n/g, ' ');
525 if (text === "") { text = this.placeholder; }
529 if (text === "") { text = this.placeholder; }
526 text = Array(this.level + 1).join("#") + " " + text;
530 text = Array(this.level + 1).join("#") + " " + text;
527 var text_and_math = IPython.mathjaxutils.remove_math(text);
531 var text_and_math = IPython.mathjaxutils.remove_math(text);
528 text = text_and_math[0];
532 text = text_and_math[0];
529 math = text_and_math[1];
533 math = text_and_math[1];
530 var html = marked.parser(marked.lexer(text));
534 var html = marked.parser(marked.lexer(text));
531 var h = $(IPython.mathjaxutils.replace_math(html, math));
535 var h = $(IPython.mathjaxutils.replace_math(html, math));
532 // add id and linkback anchor
536 // add id and linkback anchor
533 var hash = h.text().replace(/ /g, '-');
537 var hash = h.text().replace(/ /g, '-');
534 h.attr('id', hash);
538 h.attr('id', hash);
535 h.append(
539 h.append(
536 $('<a/>')
540 $('<a/>')
537 .addClass('anchor-link')
541 .addClass('anchor-link')
538 .attr('href', '#' + hash)
542 .attr('href', '#' + hash)
539 .text('¶')
543 .text('¶')
540 );
544 );
541 // TODO: This HTML needs to be treated as potentially dangerous
545
542 // user input.
546 this.set_rendered(h);
543 var rendered = this.element.find("div.text_cell_render");
544 rendered.html(h);
545 this.typeset();
547 this.typeset();
546 this.element.find('div.text_cell_input').hide();
548 this.element.find('div.text_cell_input').hide();
547 rendered.show();
549 this.element.find("div.text_cell_render").show();
548
550
549 };
551 };
550 return cont;
552 return cont;
551 };
553 };
552
554
553 IPython.TextCell = TextCell;
555 IPython.TextCell = TextCell;
554 IPython.MarkdownCell = MarkdownCell;
556 IPython.MarkdownCell = MarkdownCell;
555 IPython.RawCell = RawCell;
557 IPython.RawCell = RawCell;
556 IPython.HeadingCell = HeadingCell;
558 IPython.HeadingCell = HeadingCell;
557
559
558
560
559 return IPython;
561 return IPython;
560
562
561 }(IPython));
563 }(IPython));
562
564
General Comments 0
You need to be logged in to leave comments. Login now