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