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