##// END OF EJS Templates
remove raw cell placeholder on focus, closes #5238
Paul Ivanov -
Show More
@@ -1,550 +1,552 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 });
392 });
393
394 this.code_mirror.on('focus', function() { that.unrender(); });
393 };
395 };
394
396
395 /**
397 /**
396 * Trigger autodetection of highlight scheme for current cell
398 * Trigger autodetection of highlight scheme for current cell
397 * @method auto_highlight
399 * @method auto_highlight
398 */
400 */
399 RawCell.prototype.auto_highlight = function () {
401 RawCell.prototype.auto_highlight = function () {
400 this._auto_highlight(IPython.config.raw_cell_highlight);
402 this._auto_highlight(IPython.config.raw_cell_highlight);
401 };
403 };
402
404
403 /** @method render **/
405 /** @method render **/
404 RawCell.prototype.render = function () {
406 RawCell.prototype.render = function () {
405 // Make sure that this cell type can never be rendered
407 var cont = IPython.TextCell.prototype.render.apply(this);
406 if (this.rendered) {
408 if (cont){
407 this.unrender();
409 var text = this.get_text();
410 if (text === "") { text = this.placeholder; }
411 this.set_text(text);
408 }
412 }
409 var text = this.get_text();
413 return cont;
410 if (text === "") { text = this.placeholder; }
411 this.set_text(text);
412 };
414 };
413
415
414
416
415 /**
417 /**
416 * @class HeadingCell
418 * @class HeadingCell
417 * @extends IPython.TextCell
419 * @extends IPython.TextCell
418 */
420 */
419
421
420 /**
422 /**
421 * @constructor HeadingCell
423 * @constructor HeadingCell
422 * @extends IPython.TextCell
424 * @extends IPython.TextCell
423 */
425 */
424 var HeadingCell = function (options) {
426 var HeadingCell = function (options) {
425 options = this.mergeopt(HeadingCell, options);
427 options = this.mergeopt(HeadingCell, options);
426
428
427 this.level = 1;
429 this.level = 1;
428 this.cell_type = 'heading';
430 this.cell_type = 'heading';
429 TextCell.apply(this, [options]);
431 TextCell.apply(this, [options]);
430
432
431 /**
433 /**
432 * heading level of the cell, use getter and setter to access
434 * heading level of the cell, use getter and setter to access
433 * @property level
435 * @property level
434 */
436 */
435 };
437 };
436
438
437 HeadingCell.options_default = {
439 HeadingCell.options_default = {
438 placeholder: "Type Heading Here"
440 placeholder: "Type Heading Here"
439 };
441 };
440
442
441 HeadingCell.prototype = new TextCell();
443 HeadingCell.prototype = new TextCell();
442
444
443 /** @method fromJSON */
445 /** @method fromJSON */
444 HeadingCell.prototype.fromJSON = function (data) {
446 HeadingCell.prototype.fromJSON = function (data) {
445 if (data.level !== undefined){
447 if (data.level !== undefined){
446 this.level = data.level;
448 this.level = data.level;
447 }
449 }
448 TextCell.prototype.fromJSON.apply(this, arguments);
450 TextCell.prototype.fromJSON.apply(this, arguments);
449 };
451 };
450
452
451
453
452 /** @method toJSON */
454 /** @method toJSON */
453 HeadingCell.prototype.toJSON = function () {
455 HeadingCell.prototype.toJSON = function () {
454 var data = TextCell.prototype.toJSON.apply(this);
456 var data = TextCell.prototype.toJSON.apply(this);
455 data.level = this.get_level();
457 data.level = this.get_level();
456 return data;
458 return data;
457 };
459 };
458
460
459 /**
461 /**
460 * can the cell be split into two cells
462 * can the cell be split into two cells
461 * @method is_splittable
463 * @method is_splittable
462 **/
464 **/
463 HeadingCell.prototype.is_splittable = function () {
465 HeadingCell.prototype.is_splittable = function () {
464 return false;
466 return false;
465 };
467 };
466
468
467
469
468 /**
470 /**
469 * can the cell be merged with other cells
471 * can the cell be merged with other cells
470 * @method is_mergeable
472 * @method is_mergeable
471 **/
473 **/
472 HeadingCell.prototype.is_mergeable = function () {
474 HeadingCell.prototype.is_mergeable = function () {
473 return false;
475 return false;
474 };
476 };
475
477
476 /**
478 /**
477 * Change heading level of cell, and re-render
479 * Change heading level of cell, and re-render
478 * @method set_level
480 * @method set_level
479 */
481 */
480 HeadingCell.prototype.set_level = function (level) {
482 HeadingCell.prototype.set_level = function (level) {
481 this.level = level;
483 this.level = level;
482 if (this.rendered) {
484 if (this.rendered) {
483 this.rendered = false;
485 this.rendered = false;
484 this.render();
486 this.render();
485 }
487 }
486 };
488 };
487
489
488 /** The depth of header cell, based on html (h1 to h6)
490 /** The depth of header cell, based on html (h1 to h6)
489 * @method get_level
491 * @method get_level
490 * @return {integer} level - for 1 to 6
492 * @return {integer} level - for 1 to 6
491 */
493 */
492 HeadingCell.prototype.get_level = function () {
494 HeadingCell.prototype.get_level = function () {
493 return this.level;
495 return this.level;
494 };
496 };
495
497
496
498
497 HeadingCell.prototype.set_rendered = function (html) {
499 HeadingCell.prototype.set_rendered = function (html) {
498 this.element.find("div.text_cell_render").html(html);
500 this.element.find("div.text_cell_render").html(html);
499 };
501 };
500
502
501
503
502 HeadingCell.prototype.get_rendered = function () {
504 HeadingCell.prototype.get_rendered = function () {
503 var r = this.element.find("div.text_cell_render");
505 var r = this.element.find("div.text_cell_render");
504 return r.children().first().html();
506 return r.children().first().html();
505 };
507 };
506
508
507
509
508 HeadingCell.prototype.render = function () {
510 HeadingCell.prototype.render = function () {
509 var cont = IPython.TextCell.prototype.render.apply(this);
511 var cont = IPython.TextCell.prototype.render.apply(this);
510 if (cont) {
512 if (cont) {
511 var text = this.get_text();
513 var text = this.get_text();
512 var math = null;
514 var math = null;
513 // Markdown headings must be a single line
515 // Markdown headings must be a single line
514 text = text.replace(/\n/g, ' ');
516 text = text.replace(/\n/g, ' ');
515 if (text === "") { text = this.placeholder; }
517 if (text === "") { text = this.placeholder; }
516 text = Array(this.level + 1).join("#") + " " + text;
518 text = Array(this.level + 1).join("#") + " " + text;
517 var text_and_math = IPython.mathjaxutils.remove_math(text);
519 var text_and_math = IPython.mathjaxutils.remove_math(text);
518 text = text_and_math[0];
520 text = text_and_math[0];
519 math = text_and_math[1];
521 math = text_and_math[1];
520 var html = marked.parser(marked.lexer(text));
522 var html = marked.parser(marked.lexer(text));
521 html = IPython.mathjaxutils.replace_math(html, math);
523 html = IPython.mathjaxutils.replace_math(html, math);
522 html = security.sanitize_html(html);
524 html = security.sanitize_html(html);
523 var h = $(html);
525 var h = $(html);
524 // add id and linkback anchor
526 // add id and linkback anchor
525 var hash = h.text().replace(/ /g, '-');
527 var hash = h.text().replace(/ /g, '-');
526 h.attr('id', hash);
528 h.attr('id', hash);
527 h.append(
529 h.append(
528 $('<a/>')
530 $('<a/>')
529 .addClass('anchor-link')
531 .addClass('anchor-link')
530 .attr('href', '#' + hash)
532 .attr('href', '#' + hash)
531 .text('¶')
533 .text('¶')
532 );
534 );
533 this.set_rendered(h);
535 this.set_rendered(h);
534 this.element.find('div.input_area').hide();
536 this.element.find('div.input_area').hide();
535 this.element.find("div.text_cell_render").show();
537 this.element.find("div.text_cell_render").show();
536 this.typeset();
538 this.typeset();
537 }
539 }
538 return cont;
540 return cont;
539 };
541 };
540
542
541 IPython.TextCell = TextCell;
543 IPython.TextCell = TextCell;
542 IPython.MarkdownCell = MarkdownCell;
544 IPython.MarkdownCell = MarkdownCell;
543 IPython.RawCell = RawCell;
545 IPython.RawCell = RawCell;
544 IPython.HeadingCell = HeadingCell;
546 IPython.HeadingCell = HeadingCell;
545
547
546
548
547 return IPython;
549 return IPython;
548
550
549 }(IPython));
551 }(IPython));
550
552
General Comments 0
You need to be logged in to leave comments. Login now