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