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