##// END OF EJS Templates
Merge pull request #5310 from ivanov/fix-5238...
Min RK -
r15864:808ea631 merge
parent child Browse files
Show More
@@ -1,455 +1,459 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // TextCell
10 10 //============================================================================
11 11
12 12
13 13
14 14 /**
15 15 A module that allow to create different type of Text Cell
16 16 @module IPython
17 17 @namespace IPython
18 18 */
19 19 var IPython = (function (IPython) {
20 20 "use strict";
21 21
22 22 // TextCell base class
23 23 var keycodes = IPython.keyboard.keycodes;
24 24 var security = IPython.security;
25 25
26 26 /**
27 27 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
28 28 * cell start as not redered.
29 29 *
30 30 * @class TextCell
31 31 * @constructor TextCell
32 32 * @extend IPython.Cell
33 33 * @param {object|undefined} [options]
34 34 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config
35 35 * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass)
36 36 */
37 37 var TextCell = function (options) {
38 38 // in all TextCell/Cell subclasses
39 39 // do not assign most of members here, just pass it down
40 40 // in the options dict potentially overwriting what you wish.
41 41 // they will be assigned in the base class.
42 42
43 43 // we cannot put this as a class key as it has handle to "this".
44 44 var cm_overwrite_options = {
45 45 onKeyEvent: $.proxy(this.handle_keyevent,this)
46 46 };
47 47
48 48 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
49 49
50 50 this.cell_type = this.cell_type || 'text';
51 51
52 52 IPython.Cell.apply(this, [options]);
53 53
54 54 this.rendered = false;
55 55 };
56 56
57 57 TextCell.prototype = new IPython.Cell();
58 58
59 59 TextCell.options_default = {
60 60 cm_config : {
61 61 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
62 62 mode: 'htmlmixed',
63 63 lineWrapping : true,
64 64 }
65 65 };
66 66
67 67
68 68 /**
69 69 * Create the DOM element of the TextCell
70 70 * @method create_element
71 71 * @private
72 72 */
73 73 TextCell.prototype.create_element = function () {
74 74 IPython.Cell.prototype.create_element.apply(this, arguments);
75 75
76 76 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
77 77 cell.attr('tabindex','2');
78 78
79 79 var prompt = $('<div/>').addClass('prompt input_prompt');
80 80 cell.append(prompt);
81 81 var inner_cell = $('<div/>').addClass('inner_cell');
82 82 this.celltoolbar = new IPython.CellToolbar(this);
83 83 inner_cell.append(this.celltoolbar.element);
84 84 var input_area = $('<div/>').addClass('input_area');
85 85 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
86 86 // The tabindex=-1 makes this div focusable.
87 87 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
88 88 addClass('rendered_html').attr('tabindex','-1');
89 89 inner_cell.append(input_area).append(render_area);
90 90 cell.append(inner_cell);
91 91 this.element = cell;
92 92 };
93 93
94 94
95 95 /**
96 96 * Bind the DOM evet to cell actions
97 97 * Need to be called after TextCell.create_element
98 98 * @private
99 99 * @method bind_event
100 100 */
101 101 TextCell.prototype.bind_events = function () {
102 102 IPython.Cell.prototype.bind_events.apply(this);
103 103 var that = this;
104 104
105 105 this.element.dblclick(function () {
106 106 if (that.selected === false) {
107 107 $([IPython.events]).trigger('select.Cell', {'cell':that});
108 108 }
109 109 var cont = that.unrender();
110 110 if (cont) {
111 111 that.focus_editor();
112 112 }
113 113 });
114 114 };
115 115
116 116 // Cell level actions
117 117
118 118 TextCell.prototype.select = function () {
119 119 var cont = IPython.Cell.prototype.select.apply(this);
120 120 if (cont) {
121 121 if (this.mode === 'edit') {
122 122 this.code_mirror.refresh();
123 123 }
124 124 }
125 125 return cont;
126 126 };
127 127
128 128 TextCell.prototype.unrender = function () {
129 129 if (this.read_only) return;
130 130 var cont = IPython.Cell.prototype.unrender.apply(this);
131 131 if (cont) {
132 132 var text_cell = this.element;
133 133 var output = text_cell.find("div.text_cell_render");
134 134 output.hide();
135 135 text_cell.find('div.input_area').show();
136 136 if (this.get_text() === this.placeholder) {
137 137 this.set_text('');
138 138 }
139 139 this.refresh();
140 140 }
141 141 return cont;
142 142 };
143 143
144 144 TextCell.prototype.execute = function () {
145 145 this.render();
146 146 };
147 147
148 148 /**
149 149 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
150 150 * @method get_text
151 151 * @retrun {string} CodeMirror current text value
152 152 */
153 153 TextCell.prototype.get_text = function() {
154 154 return this.code_mirror.getValue();
155 155 };
156 156
157 157 /**
158 158 * @param {string} text - Codemiror text value
159 159 * @see TextCell#get_text
160 160 * @method set_text
161 161 * */
162 162 TextCell.prototype.set_text = function(text) {
163 163 this.code_mirror.setValue(text);
164 164 this.code_mirror.refresh();
165 165 };
166 166
167 167 /**
168 168 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
169 169 * @method get_rendered
170 170 * @return {html} html of rendered element
171 171 * */
172 172 TextCell.prototype.get_rendered = function() {
173 173 return this.element.find('div.text_cell_render').html();
174 174 };
175 175
176 176 /**
177 177 * @method set_rendered
178 178 */
179 179 TextCell.prototype.set_rendered = function(text) {
180 180 this.element.find('div.text_cell_render').html(text);
181 181 };
182 182
183 183
184 184 /**
185 185 * Create Text cell from JSON
186 186 * @param {json} data - JSON serialized text-cell
187 187 * @method fromJSON
188 188 */
189 189 TextCell.prototype.fromJSON = function (data) {
190 190 IPython.Cell.prototype.fromJSON.apply(this, arguments);
191 191 if (data.cell_type === this.cell_type) {
192 192 if (data.source !== undefined) {
193 193 this.set_text(data.source);
194 194 // make this value the starting point, so that we can only undo
195 195 // to this state, instead of a blank cell
196 196 this.code_mirror.clearHistory();
197 197 // TODO: This HTML needs to be treated as potentially dangerous
198 198 // user input and should be handled before set_rendered.
199 199 this.set_rendered(data.rendered || '');
200 200 this.rendered = false;
201 201 this.render();
202 202 }
203 203 }
204 204 };
205 205
206 206 /** Generate JSON from cell
207 207 * @return {object} cell data serialised to json
208 208 */
209 209 TextCell.prototype.toJSON = function () {
210 210 var data = IPython.Cell.prototype.toJSON.apply(this);
211 211 data.source = this.get_text();
212 212 if (data.source == this.placeholder) {
213 213 data.source = "";
214 214 }
215 215 return data;
216 216 };
217 217
218 218
219 219 /**
220 220 * @class MarkdownCell
221 221 * @constructor MarkdownCell
222 222 * @extends IPython.HTMLCell
223 223 */
224 224 var MarkdownCell = function (options) {
225 225 options = this.mergeopt(MarkdownCell, options);
226 226
227 227 this.cell_type = 'markdown';
228 228 TextCell.apply(this, [options]);
229 229 };
230 230
231 231 MarkdownCell.options_default = {
232 232 cm_config: {
233 233 mode: 'gfm'
234 234 },
235 235 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
236 236 };
237 237
238 238 MarkdownCell.prototype = new TextCell();
239 239
240 240 /**
241 241 * @method render
242 242 */
243 243 MarkdownCell.prototype.render = function () {
244 244 var cont = IPython.TextCell.prototype.render.apply(this);
245 245 if (cont) {
246 246 var text = this.get_text();
247 247 var math = null;
248 248 if (text === "") { text = this.placeholder; }
249 249 var text_and_math = IPython.mathjaxutils.remove_math(text);
250 250 text = text_and_math[0];
251 251 math = text_and_math[1];
252 252 var html = marked.parser(marked.lexer(text));
253 253 html = IPython.mathjaxutils.replace_math(html, math);
254 254 html = security.sanitize_html(html);
255 255 html = $(html);
256 256 // links in markdown cells should open in new tabs
257 257 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
258 258 this.set_rendered(html);
259 259 this.element.find('div.input_area').hide();
260 260 this.element.find("div.text_cell_render").show();
261 261 this.typeset();
262 262 }
263 263 return cont;
264 264 };
265 265
266 266
267 267 // RawCell
268 268
269 269 /**
270 270 * @class RawCell
271 271 * @constructor RawCell
272 272 * @extends IPython.TextCell
273 273 */
274 274 var RawCell = function (options) {
275 275
276 276 options = this.mergeopt(RawCell,options);
277 277 TextCell.apply(this, [options]);
278 278 this.cell_type = 'raw';
279 279 // RawCell should always hide its rendered div
280 280 this.element.find('div.text_cell_render').hide();
281 281 };
282 282
283 283 RawCell.options_default = {
284 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert.\n" +
285 "It will not be rendered in the notebook.\n" +
284 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
285 "It will not be rendered in the notebook. " +
286 286 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
287 287 };
288 288
289 289 RawCell.prototype = new TextCell();
290 290
291 291 /** @method bind_events **/
292 292 RawCell.prototype.bind_events = function () {
293 293 TextCell.prototype.bind_events.apply(this);
294 294 var that = this;
295 295 this.element.focusout(function() {
296 296 that.auto_highlight();
297 that.render();
297 298 });
299
300 this.code_mirror.on('focus', function() { that.unrender(); });
298 301 };
299 302
300 303 /**
301 304 * Trigger autodetection of highlight scheme for current cell
302 305 * @method auto_highlight
303 306 */
304 307 RawCell.prototype.auto_highlight = function () {
305 308 this._auto_highlight(IPython.config.raw_cell_highlight);
306 309 };
307 310
308 311 /** @method render **/
309 312 RawCell.prototype.render = function () {
310 // Make sure that this cell type can never be rendered
311 if (this.rendered) {
312 this.unrender();
313 }
313 var cont = IPython.TextCell.prototype.render.apply(this);
314 if (cont){
314 315 var text = this.get_text();
315 316 if (text === "") { text = this.placeholder; }
316 317 this.set_text(text);
318 this.element.removeClass('rendered');
319 }
320 return cont;
317 321 };
318 322
319 323
320 324 /**
321 325 * @class HeadingCell
322 326 * @extends IPython.TextCell
323 327 */
324 328
325 329 /**
326 330 * @constructor HeadingCell
327 331 * @extends IPython.TextCell
328 332 */
329 333 var HeadingCell = function (options) {
330 334 options = this.mergeopt(HeadingCell, options);
331 335
332 336 this.level = 1;
333 337 this.cell_type = 'heading';
334 338 TextCell.apply(this, [options]);
335 339
336 340 /**
337 341 * heading level of the cell, use getter and setter to access
338 342 * @property level
339 343 */
340 344 };
341 345
342 346 HeadingCell.options_default = {
343 347 placeholder: "Type Heading Here"
344 348 };
345 349
346 350 HeadingCell.prototype = new TextCell();
347 351
348 352 /** @method fromJSON */
349 353 HeadingCell.prototype.fromJSON = function (data) {
350 354 if (data.level !== undefined){
351 355 this.level = data.level;
352 356 }
353 357 TextCell.prototype.fromJSON.apply(this, arguments);
354 358 };
355 359
356 360
357 361 /** @method toJSON */
358 362 HeadingCell.prototype.toJSON = function () {
359 363 var data = TextCell.prototype.toJSON.apply(this);
360 364 data.level = this.get_level();
361 365 return data;
362 366 };
363 367
364 368 /**
365 369 * can the cell be split into two cells
366 370 * @method is_splittable
367 371 **/
368 372 HeadingCell.prototype.is_splittable = function () {
369 373 return false;
370 374 };
371 375
372 376
373 377 /**
374 378 * can the cell be merged with other cells
375 379 * @method is_mergeable
376 380 **/
377 381 HeadingCell.prototype.is_mergeable = function () {
378 382 return false;
379 383 };
380 384
381 385 /**
382 386 * Change heading level of cell, and re-render
383 387 * @method set_level
384 388 */
385 389 HeadingCell.prototype.set_level = function (level) {
386 390 this.level = level;
387 391 if (this.rendered) {
388 392 this.rendered = false;
389 393 this.render();
390 394 }
391 395 };
392 396
393 397 /** The depth of header cell, based on html (h1 to h6)
394 398 * @method get_level
395 399 * @return {integer} level - for 1 to 6
396 400 */
397 401 HeadingCell.prototype.get_level = function () {
398 402 return this.level;
399 403 };
400 404
401 405
402 406 HeadingCell.prototype.set_rendered = function (html) {
403 407 this.element.find("div.text_cell_render").html(html);
404 408 };
405 409
406 410
407 411 HeadingCell.prototype.get_rendered = function () {
408 412 var r = this.element.find("div.text_cell_render");
409 413 return r.children().first().html();
410 414 };
411 415
412 416
413 417 HeadingCell.prototype.render = function () {
414 418 var cont = IPython.TextCell.prototype.render.apply(this);
415 419 if (cont) {
416 420 var text = this.get_text();
417 421 var math = null;
418 422 // Markdown headings must be a single line
419 423 text = text.replace(/\n/g, ' ');
420 424 if (text === "") { text = this.placeholder; }
421 425 text = Array(this.level + 1).join("#") + " " + text;
422 426 var text_and_math = IPython.mathjaxutils.remove_math(text);
423 427 text = text_and_math[0];
424 428 math = text_and_math[1];
425 429 var html = marked.parser(marked.lexer(text));
426 430 html = IPython.mathjaxutils.replace_math(html, math);
427 431 html = security.sanitize_html(html);
428 432 var h = $(html);
429 433 // add id and linkback anchor
430 434 var hash = h.text().replace(/ /g, '-');
431 435 h.attr('id', hash);
432 436 h.append(
433 437 $('<a/>')
434 438 .addClass('anchor-link')
435 439 .attr('href', '#' + hash)
436 440 .text('¶')
437 441 );
438 442 this.set_rendered(h);
439 443 this.element.find('div.input_area').hide();
440 444 this.element.find("div.text_cell_render").show();
441 445 this.typeset();
442 446 }
443 447 return cont;
444 448 };
445 449
446 450 IPython.TextCell = TextCell;
447 451 IPython.MarkdownCell = MarkdownCell;
448 452 IPython.RawCell = RawCell;
449 453 IPython.HeadingCell = HeadingCell;
450 454
451 455
452 456 return IPython;
453 457
454 458 }(IPython));
455 459
General Comments 0
You need to be logged in to leave comments. Login now