##// END OF EJS Templates
Transfer of border-box-sizing from js to css...
Matthias BUSSONNIER -
Show More
@@ -1,526 +1,526 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 8 'base/js/keyboard',
9 9 'notebook/js/cell',
10 10 'notebook/js/outputarea',
11 11 'notebook/js/completer',
12 12 'notebook/js/celltoolbar',
13 13 ], function(IPython, $, utils, keyboard, cell, outputarea, completer, celltoolbar) {
14 14 "use strict";
15 15 var Cell = cell.Cell;
16 16
17 17 /* local util for codemirror */
18 18 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
19 19
20 20 /**
21 21 *
22 22 * function to delete until previous non blanking space character
23 23 * or first multiple of 4 tabstop.
24 24 * @private
25 25 */
26 26 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
27 27 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
28 28 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
29 29 var cur = cm.getCursor(), line = cm.getLine(cur.line);
30 30 var tabsize = cm.getOption('tabSize');
31 31 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
32 32 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
33 33 var select = cm.getRange(from,cur);
34 34 if( select.match(/^\ +$/) !== null){
35 35 cm.replaceRange("",from,cur);
36 36 } else {
37 37 cm.deleteH(-1,"char");
38 38 }
39 39 };
40 40
41 41 var keycodes = keyboard.keycodes;
42 42
43 43 var CodeCell = function (kernel, options) {
44 44 // Constructor
45 45 //
46 46 // A Cell conceived to write code.
47 47 //
48 48 // Parameters:
49 49 // kernel: Kernel instance
50 50 // The kernel doesn't have to be set at creation time, in that case
51 51 // it will be null and set_kernel has to be called later.
52 52 // options: dictionary
53 53 // Dictionary of keyword arguments.
54 54 // events: $(Events) instance
55 55 // config: dictionary
56 56 // keyboard_manager: KeyboardManager instance
57 57 // notebook: Notebook instance
58 58 // tooltip: Tooltip instance
59 59 this.kernel = kernel || null;
60 60 this.notebook = options.notebook;
61 61 this.collapsed = false;
62 62 this.events = options.events;
63 63 this.tooltip = options.tooltip;
64 64 this.config = options.config;
65 65
66 66 // create all attributed in constructor function
67 67 // even if null for V8 VM optimisation
68 68 this.input_prompt_number = null;
69 69 this.celltoolbar = null;
70 70 this.output_area = null;
71 71 this.last_msg_id = null;
72 72 this.completer = null;
73 73
74 74
75 75 var cm_overwrite_options = {
76 76 onKeyEvent: $.proxy(this.handle_keyevent,this)
77 77 };
78 78
79 79 var config = this.mergeopt(CodeCell, this.config, {cm_config: cm_overwrite_options});
80 80 Cell.apply(this,[{
81 81 config: config,
82 82 keyboard_manager: options.keyboard_manager,
83 83 events: this.events}]);
84 84
85 85 // Attributes we want to override in this subclass.
86 86 this.cell_type = "code";
87 87
88 88 var that = this;
89 89 this.element.focusout(
90 90 function() { that.auto_highlight(); }
91 91 );
92 92 };
93 93
94 94 CodeCell.options_default = {
95 95 cm_config : {
96 96 extraKeys: {
97 97 "Tab" : "indentMore",
98 98 "Shift-Tab" : "indentLess",
99 99 "Backspace" : "delSpaceToPrevTabStop",
100 100 "Cmd-/" : "toggleComment",
101 101 "Ctrl-/" : "toggleComment"
102 102 },
103 103 mode: 'ipython',
104 104 theme: 'ipython',
105 105 matchBrackets: true,
106 106 // don't auto-close strings because of CodeMirror #2385
107 107 autoCloseBrackets: "()[]{}"
108 108 }
109 109 };
110 110
111 111 CodeCell.msg_cells = {};
112 112
113 113 CodeCell.prototype = new Cell();
114 114
115 115 /**
116 116 * @method auto_highlight
117 117 */
118 118 CodeCell.prototype.auto_highlight = function () {
119 119 this._auto_highlight(this.config.cell_magic_highlight);
120 120 };
121 121
122 122 /** @method create_element */
123 123 CodeCell.prototype.create_element = function () {
124 124 Cell.prototype.create_element.apply(this, arguments);
125 125
126 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
126 var cell = $('<div></div>').addClass('cell code_cell');
127 127 cell.attr('tabindex','2');
128 128
129 129 var input = $('<div></div>').addClass('input');
130 130 var prompt = $('<div/>').addClass('prompt input_prompt');
131 131 var inner_cell = $('<div/>').addClass('inner_cell');
132 132 this.celltoolbar = new celltoolbar.CellToolbar({
133 133 cell: this,
134 134 events: this.events,
135 135 notebook: this.notebook});
136 136 inner_cell.append(this.celltoolbar.element);
137 137 var input_area = $('<div/>').addClass('input_area');
138 138 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
139 139 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
140 140 inner_cell.append(input_area);
141 141 input.append(prompt).append(inner_cell);
142 142
143 143 var widget_area = $('<div/>')
144 144 .addClass('widget-area')
145 145 .hide();
146 146 this.widget_area = widget_area;
147 147 var widget_prompt = $('<div/>')
148 148 .addClass('prompt')
149 149 .appendTo(widget_area);
150 150 var widget_subarea = $('<div/>')
151 151 .addClass('widget-subarea')
152 152 .appendTo(widget_area);
153 153 this.widget_subarea = widget_subarea;
154 154 var widget_clear_buton = $('<button />')
155 155 .addClass('close')
156 156 .html('&times;')
157 157 .click(function() {
158 158 widget_area.slideUp('', function(){ widget_subarea.html(''); });
159 159 })
160 160 .appendTo(widget_prompt);
161 161
162 162 var output = $('<div></div>');
163 163 cell.append(input).append(widget_area).append(output);
164 164 this.element = cell;
165 165 this.output_area = new outputarea.OutputArea({
166 166 selector: output,
167 167 prompt_area: true,
168 168 events: this.events,
169 169 keyboard_manager: this.keyboard_manager});
170 170 this.completer = new completer.Completer(this, this.events);
171 171 };
172 172
173 173 /** @method bind_events */
174 174 CodeCell.prototype.bind_events = function () {
175 175 Cell.prototype.bind_events.apply(this);
176 176 var that = this;
177 177
178 178 this.element.focusout(
179 179 function() { that.auto_highlight(); }
180 180 );
181 181 };
182 182
183 183
184 184 /**
185 185 * This method gets called in CodeMirror's onKeyDown/onKeyPress
186 186 * handlers and is used to provide custom key handling. Its return
187 187 * value is used to determine if CodeMirror should ignore the event:
188 188 * true = ignore, false = don't ignore.
189 189 * @method handle_codemirror_keyevent
190 190 */
191 191 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
192 192
193 193 var that = this;
194 194 // whatever key is pressed, first, cancel the tooltip request before
195 195 // they are sent, and remove tooltip if any, except for tab again
196 196 var tooltip_closed = null;
197 197 if (event.type === 'keydown' && event.which != keycodes.tab ) {
198 198 tooltip_closed = this.tooltip.remove_and_cancel_tooltip();
199 199 }
200 200
201 201 var cur = editor.getCursor();
202 202 if (event.keyCode === keycodes.enter){
203 203 this.auto_highlight();
204 204 }
205 205
206 206 if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) {
207 207 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
208 208 // browser and keyboard layout !
209 209 // Pressing '(' , request tooltip, don't forget to reappend it
210 210 // The second argument says to hide the tooltip if the docstring
211 211 // is actually empty
212 212 this.tooltip.pending(that, true);
213 213 } else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
214 214 // If tooltip is active, cancel it. The call to
215 215 // remove_and_cancel_tooltip above doesn't pass, force=true.
216 216 // Because of this it won't actually close the tooltip
217 217 // if it is in sticky mode. Thus, we have to check again if it is open
218 218 // and close it with force=true.
219 219 if (!this.tooltip._hidden) {
220 220 this.tooltip.remove_and_cancel_tooltip(true);
221 221 }
222 222 // If we closed the tooltip, don't let CM or the global handlers
223 223 // handle this event.
224 224 event.stop();
225 225 return true;
226 226 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
227 227 if (editor.somethingSelected()){
228 228 var anchor = editor.getCursor("anchor");
229 229 var head = editor.getCursor("head");
230 230 if( anchor.line != head.line){
231 231 return false;
232 232 }
233 233 }
234 234 this.tooltip.request(that);
235 235 event.stop();
236 236 return true;
237 237 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
238 238 // Tab completion.
239 239 this.tooltip.remove_and_cancel_tooltip();
240 240 if (editor.somethingSelected()) {
241 241 return false;
242 242 }
243 243 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
244 244 if (pre_cursor.trim() === "") {
245 245 // Don't autocomplete if the part of the line before the cursor
246 246 // is empty. In this case, let CodeMirror handle indentation.
247 247 return false;
248 248 } else {
249 249 event.stop();
250 250 this.completer.startCompletion();
251 251 return true;
252 252 }
253 253 }
254 254
255 255 // keyboard event wasn't one of those unique to code cells, let's see
256 256 // if it's one of the generic ones (i.e. check edit mode shortcuts)
257 257 return Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
258 258 };
259 259
260 260 // Kernel related calls.
261 261
262 262 CodeCell.prototype.set_kernel = function (kernel) {
263 263 this.kernel = kernel;
264 264 };
265 265
266 266 /**
267 267 * Execute current code cell to the kernel
268 268 * @method execute
269 269 */
270 270 CodeCell.prototype.execute = function () {
271 271 this.output_area.clear_output();
272 272
273 273 // Clear widget area
274 274 this.widget_subarea.html('');
275 275 this.widget_subarea.height('');
276 276 this.widget_area.height('');
277 277 this.widget_area.hide();
278 278
279 279 this.set_input_prompt('*');
280 280 this.element.addClass("running");
281 281 if (this.last_msg_id) {
282 282 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
283 283 }
284 284 var callbacks = this.get_callbacks();
285 285
286 286 var old_msg_id = this.last_msg_id;
287 287 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
288 288 if (old_msg_id) {
289 289 delete CodeCell.msg_cells[old_msg_id];
290 290 }
291 291 CodeCell.msg_cells[this.last_msg_id] = this;
292 292 };
293 293
294 294 /**
295 295 * Construct the default callbacks for
296 296 * @method get_callbacks
297 297 */
298 298 CodeCell.prototype.get_callbacks = function () {
299 299 return {
300 300 shell : {
301 301 reply : $.proxy(this._handle_execute_reply, this),
302 302 payload : {
303 303 set_next_input : $.proxy(this._handle_set_next_input, this),
304 304 page : $.proxy(this._open_with_pager, this)
305 305 }
306 306 },
307 307 iopub : {
308 308 output : $.proxy(this.output_area.handle_output, this.output_area),
309 309 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
310 310 },
311 311 input : $.proxy(this._handle_input_request, this)
312 312 };
313 313 };
314 314
315 315 CodeCell.prototype._open_with_pager = function (payload) {
316 316 this.events.trigger('open_with_text.Pager', payload);
317 317 };
318 318
319 319 /**
320 320 * @method _handle_execute_reply
321 321 * @private
322 322 */
323 323 CodeCell.prototype._handle_execute_reply = function (msg) {
324 324 this.set_input_prompt(msg.content.execution_count);
325 325 this.element.removeClass("running");
326 326 this.events.trigger('set_dirty.Notebook', {value: true});
327 327 };
328 328
329 329 /**
330 330 * @method _handle_set_next_input
331 331 * @private
332 332 */
333 333 CodeCell.prototype._handle_set_next_input = function (payload) {
334 334 var data = {'cell': this, 'text': payload.text};
335 335 this.events.trigger('set_next_input.Notebook', data);
336 336 };
337 337
338 338 /**
339 339 * @method _handle_input_request
340 340 * @private
341 341 */
342 342 CodeCell.prototype._handle_input_request = function (msg) {
343 343 this.output_area.append_raw_input(msg);
344 344 };
345 345
346 346
347 347 // Basic cell manipulation.
348 348
349 349 CodeCell.prototype.select = function () {
350 350 var cont = Cell.prototype.select.apply(this);
351 351 if (cont) {
352 352 this.code_mirror.refresh();
353 353 this.auto_highlight();
354 354 }
355 355 return cont;
356 356 };
357 357
358 358 CodeCell.prototype.render = function () {
359 359 var cont = Cell.prototype.render.apply(this);
360 360 // Always execute, even if we are already in the rendered state
361 361 return cont;
362 362 };
363 363
364 364 CodeCell.prototype.unrender = function () {
365 365 // CodeCell is always rendered
366 366 return false;
367 367 };
368 368
369 369 CodeCell.prototype.select_all = function () {
370 370 var start = {line: 0, ch: 0};
371 371 var nlines = this.code_mirror.lineCount();
372 372 var last_line = this.code_mirror.getLine(nlines-1);
373 373 var end = {line: nlines-1, ch: last_line.length};
374 374 this.code_mirror.setSelection(start, end);
375 375 };
376 376
377 377
378 378 CodeCell.prototype.collapse_output = function () {
379 379 this.collapsed = true;
380 380 this.output_area.collapse();
381 381 };
382 382
383 383
384 384 CodeCell.prototype.expand_output = function () {
385 385 this.collapsed = false;
386 386 this.output_area.expand();
387 387 this.output_area.unscroll_area();
388 388 };
389 389
390 390 CodeCell.prototype.scroll_output = function () {
391 391 this.output_area.expand();
392 392 this.output_area.scroll_if_long();
393 393 };
394 394
395 395 CodeCell.prototype.toggle_output = function () {
396 396 this.collapsed = Boolean(1 - this.collapsed);
397 397 this.output_area.toggle_output();
398 398 };
399 399
400 400 CodeCell.prototype.toggle_output_scroll = function () {
401 401 this.output_area.toggle_scroll();
402 402 };
403 403
404 404
405 405 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
406 406 var ns;
407 407 if (prompt_value === undefined) {
408 408 ns = "&nbsp;";
409 409 } else {
410 410 ns = encodeURIComponent(prompt_value);
411 411 }
412 412 return 'In&nbsp;[' + ns + ']:';
413 413 };
414 414
415 415 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
416 416 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
417 417 for(var i=1; i < lines_number; i++) {
418 418 html.push(['...:']);
419 419 }
420 420 return html.join('<br/>');
421 421 };
422 422
423 423 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
424 424
425 425
426 426 CodeCell.prototype.set_input_prompt = function (number) {
427 427 var nline = 1;
428 428 if (this.code_mirror !== undefined) {
429 429 nline = this.code_mirror.lineCount();
430 430 }
431 431 this.input_prompt_number = number;
432 432 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
433 433 // This HTML call is okay because the user contents are escaped.
434 434 this.element.find('div.input_prompt').html(prompt_html);
435 435 };
436 436
437 437
438 438 CodeCell.prototype.clear_input = function () {
439 439 this.code_mirror.setValue('');
440 440 };
441 441
442 442
443 443 CodeCell.prototype.get_text = function () {
444 444 return this.code_mirror.getValue();
445 445 };
446 446
447 447
448 448 CodeCell.prototype.set_text = function (code) {
449 449 return this.code_mirror.setValue(code);
450 450 };
451 451
452 452
453 453 CodeCell.prototype.clear_output = function (wait) {
454 454 this.output_area.clear_output(wait);
455 455 this.set_input_prompt();
456 456 };
457 457
458 458
459 459 // JSON serialization
460 460
461 461 CodeCell.prototype.fromJSON = function (data) {
462 462 Cell.prototype.fromJSON.apply(this, arguments);
463 463 if (data.cell_type === 'code') {
464 464 if (data.input !== undefined) {
465 465 this.set_text(data.input);
466 466 // make this value the starting point, so that we can only undo
467 467 // to this state, instead of a blank cell
468 468 this.code_mirror.clearHistory();
469 469 this.auto_highlight();
470 470 }
471 471 if (data.prompt_number !== undefined) {
472 472 this.set_input_prompt(data.prompt_number);
473 473 } else {
474 474 this.set_input_prompt();
475 475 }
476 476 this.output_area.trusted = data.trusted || false;
477 477 this.output_area.fromJSON(data.outputs);
478 478 if (data.collapsed !== undefined) {
479 479 if (data.collapsed) {
480 480 this.collapse_output();
481 481 } else {
482 482 this.expand_output();
483 483 }
484 484 }
485 485 }
486 486 };
487 487
488 488
489 489 CodeCell.prototype.toJSON = function () {
490 490 var data = Cell.prototype.toJSON.apply(this);
491 491 data.input = this.get_text();
492 492 // is finite protect against undefined and '*' value
493 493 if (isFinite(this.input_prompt_number)) {
494 494 data.prompt_number = this.input_prompt_number;
495 495 }
496 496 var outputs = this.output_area.toJSON();
497 497 data.outputs = outputs;
498 498 data.language = 'python';
499 499 data.trusted = this.output_area.trusted;
500 500 data.collapsed = this.collapsed;
501 501 return data;
502 502 };
503 503
504 504 /**
505 505 * handle cell level logic when a cell is unselected
506 506 * @method unselect
507 507 * @return is the action being taken
508 508 */
509 509 CodeCell.prototype.unselect = function () {
510 510 var cont = Cell.prototype.unselect.apply(this);
511 511 if (cont) {
512 512 // When a code cell is usnelected, make sure that the corresponding
513 513 // tooltip and completer to that cell is closed.
514 514 this.tooltip.remove_and_cancel_tooltip(true);
515 515 if (this.completer !== null) {
516 516 this.completer.close();
517 517 }
518 518 }
519 519 return cont;
520 520 };
521 521
522 522 // Backwards compatability.
523 523 IPython.CodeCell = CodeCell;
524 524
525 525 return {'CodeCell': CodeCell};
526 526 });
@@ -1,464 +1,464 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'notebook/js/cell',
8 8 'base/js/security',
9 9 'notebook/js/mathjaxutils',
10 10 'notebook/js/celltoolbar',
11 11 'components/marked/lib/marked',
12 12 ], function(IPython, $, cell, security, mathjaxutils, celltoolbar, marked) {
13 13 "use strict";
14 14 var Cell = cell.Cell;
15 15
16 16 var TextCell = function (options) {
17 17 // Constructor
18 18 //
19 19 // Construct a new TextCell, codemirror mode is by default 'htmlmixed',
20 20 // and cell type is 'text' cell start as not redered.
21 21 //
22 22 // Parameters:
23 23 // options: dictionary
24 24 // Dictionary of keyword arguments.
25 25 // events: $(Events) instance
26 26 // config: dictionary
27 27 // keyboard_manager: KeyboardManager instance
28 28 // notebook: Notebook instance
29 29 options = options || {};
30 30
31 31 // in all TextCell/Cell subclasses
32 32 // do not assign most of members here, just pass it down
33 33 // in the options dict potentially overwriting what you wish.
34 34 // they will be assigned in the base class.
35 35 this.notebook = options.notebook;
36 36 this.events = options.events;
37 37 this.config = options.config;
38 38
39 39 // we cannot put this as a class key as it has handle to "this".
40 40 var cm_overwrite_options = {
41 41 onKeyEvent: $.proxy(this.handle_keyevent,this)
42 42 };
43 43 var config = this.mergeopt(TextCell, this.config, {cm_config:cm_overwrite_options});
44 44 Cell.apply(this, [{
45 45 config: config,
46 46 keyboard_manager: options.keyboard_manager,
47 47 events: this.events}]);
48 48
49 49 this.cell_type = this.cell_type || 'text';
50 50 mathjaxutils = mathjaxutils;
51 51 this.rendered = false;
52 52 };
53 53
54 54 TextCell.prototype = new Cell();
55 55
56 56 TextCell.options_default = {
57 57 cm_config : {
58 58 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
59 59 mode: 'htmlmixed',
60 60 lineWrapping : true,
61 61 }
62 62 };
63 63
64 64
65 65 /**
66 66 * Create the DOM element of the TextCell
67 67 * @method create_element
68 68 * @private
69 69 */
70 70 TextCell.prototype.create_element = function () {
71 71 Cell.prototype.create_element.apply(this, arguments);
72 72
73 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
73 var cell = $("<div>").addClass('cell text_cell');
74 74 cell.attr('tabindex','2');
75 75
76 76 var prompt = $('<div/>').addClass('prompt input_prompt');
77 77 cell.append(prompt);
78 78 var inner_cell = $('<div/>').addClass('inner_cell');
79 79 this.celltoolbar = new celltoolbar.CellToolbar({
80 80 cell: this,
81 81 events: this.events,
82 82 notebook: this.notebook});
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 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 this.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 = 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 = 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 if (this.celltoolbar.ui_controls_list.length) {
142 142 this.celltoolbar.show();
143 143 }
144 144 return cont;
145 145 };
146 146
147 147 TextCell.prototype.execute = function () {
148 148 this.render();
149 149 };
150 150
151 151 /**
152 152 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
153 153 * @method get_text
154 154 * @retrun {string} CodeMirror current text value
155 155 */
156 156 TextCell.prototype.get_text = function() {
157 157 return this.code_mirror.getValue();
158 158 };
159 159
160 160 /**
161 161 * @param {string} text - Codemiror text value
162 162 * @see TextCell#get_text
163 163 * @method set_text
164 164 * */
165 165 TextCell.prototype.set_text = function(text) {
166 166 this.code_mirror.setValue(text);
167 167 this.code_mirror.refresh();
168 168 };
169 169
170 170 /**
171 171 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
172 172 * @method get_rendered
173 173 * */
174 174 TextCell.prototype.get_rendered = function() {
175 175 return this.element.find('div.text_cell_render').html();
176 176 };
177 177
178 178 /**
179 179 * @method set_rendered
180 180 */
181 181 TextCell.prototype.set_rendered = function(text) {
182 182 this.element.find('div.text_cell_render').html(text);
183 183 this.celltoolbar.hide();
184 184 };
185 185
186 186
187 187 /**
188 188 * Create Text cell from JSON
189 189 * @param {json} data - JSON serialized text-cell
190 190 * @method fromJSON
191 191 */
192 192 TextCell.prototype.fromJSON = function (data) {
193 193 Cell.prototype.fromJSON.apply(this, arguments);
194 194 if (data.cell_type === this.cell_type) {
195 195 if (data.source !== undefined) {
196 196 this.set_text(data.source);
197 197 // make this value the starting point, so that we can only undo
198 198 // to this state, instead of a blank cell
199 199 this.code_mirror.clearHistory();
200 200 // TODO: This HTML needs to be treated as potentially dangerous
201 201 // user input and should be handled before set_rendered.
202 202 this.set_rendered(data.rendered || '');
203 203 this.rendered = false;
204 204 this.render();
205 205 }
206 206 }
207 207 };
208 208
209 209 /** Generate JSON from cell
210 210 * @return {object} cell data serialised to json
211 211 */
212 212 TextCell.prototype.toJSON = function () {
213 213 var data = Cell.prototype.toJSON.apply(this);
214 214 data.source = this.get_text();
215 215 if (data.source == this.placeholder) {
216 216 data.source = "";
217 217 }
218 218 return data;
219 219 };
220 220
221 221
222 222 var MarkdownCell = function (options) {
223 223 // Constructor
224 224 //
225 225 // Parameters:
226 226 // options: dictionary
227 227 // Dictionary of keyword arguments.
228 228 // events: $(Events) instance
229 229 // config: dictionary
230 230 // keyboard_manager: KeyboardManager instance
231 231 // notebook: Notebook instance
232 232 options = options || {};
233 233 var config = this.mergeopt(MarkdownCell, options.config);
234 234 TextCell.apply(this, [$.extend({}, options, {config: config})]);
235 235
236 236 this.cell_type = 'markdown';
237 237 };
238 238
239 239 MarkdownCell.options_default = {
240 240 cm_config: {
241 241 mode: 'ipythongfm'
242 242 },
243 243 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
244 244 };
245 245
246 246 MarkdownCell.prototype = new TextCell();
247 247
248 248 /**
249 249 * @method render
250 250 */
251 251 MarkdownCell.prototype.render = function () {
252 252 var cont = TextCell.prototype.render.apply(this);
253 253 if (cont) {
254 254 var text = this.get_text();
255 255 var math = null;
256 256 if (text === "") { text = this.placeholder; }
257 257 var text_and_math = mathjaxutils.remove_math(text);
258 258 text = text_and_math[0];
259 259 math = text_and_math[1];
260 260 var html = marked.parser(marked.lexer(text));
261 261 html = mathjaxutils.replace_math(html, math);
262 262 html = security.sanitize_html(html);
263 263 html = $($.parseHTML(html));
264 264 // links in markdown cells should open in new tabs
265 265 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
266 266 this.set_rendered(html);
267 267 this.element.find('div.input_area').hide();
268 268 this.element.find("div.text_cell_render").show();
269 269 this.typeset();
270 270 }
271 271 return cont;
272 272 };
273 273
274 274
275 275 var RawCell = function (options) {
276 276 // Constructor
277 277 //
278 278 // Parameters:
279 279 // options: dictionary
280 280 // Dictionary of keyword arguments.
281 281 // events: $(Events) instance
282 282 // config: dictionary
283 283 // keyboard_manager: KeyboardManager instance
284 284 // notebook: Notebook instance
285 285 options = options || {};
286 286 var config = this.mergeopt(RawCell, options.config);
287 287 TextCell.apply(this, [$.extend({}, options, {config: config})]);
288 288
289 289 // RawCell should always hide its rendered div
290 290 this.element.find('div.text_cell_render').hide();
291 291 this.cell_type = 'raw';
292 292 };
293 293
294 294 RawCell.options_default = {
295 295 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
296 296 "It will not be rendered in the notebook. " +
297 297 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
298 298 };
299 299
300 300 RawCell.prototype = new TextCell();
301 301
302 302 /** @method bind_events **/
303 303 RawCell.prototype.bind_events = function () {
304 304 TextCell.prototype.bind_events.apply(this);
305 305 var that = this;
306 306 this.element.focusout(function() {
307 307 that.auto_highlight();
308 308 that.render();
309 309 });
310 310
311 311 this.code_mirror.on('focus', function() { that.unrender(); });
312 312 };
313 313
314 314 /**
315 315 * Trigger autodetection of highlight scheme for current cell
316 316 * @method auto_highlight
317 317 */
318 318 RawCell.prototype.auto_highlight = function () {
319 319 this._auto_highlight(this.config.raw_cell_highlight);
320 320 };
321 321
322 322 /** @method render **/
323 323 RawCell.prototype.render = function () {
324 324 var cont = TextCell.prototype.render.apply(this);
325 325 if (cont){
326 326 var text = this.get_text();
327 327 if (text === "") { text = this.placeholder; }
328 328 this.set_text(text);
329 329 this.element.removeClass('rendered');
330 330 }
331 331 return cont;
332 332 };
333 333
334 334
335 335 var HeadingCell = function (options) {
336 336 // Constructor
337 337 //
338 338 // Parameters:
339 339 // options: dictionary
340 340 // Dictionary of keyword arguments.
341 341 // events: $(Events) instance
342 342 // config: dictionary
343 343 // keyboard_manager: KeyboardManager instance
344 344 // notebook: Notebook instance
345 345 options = options || {};
346 346 var config = this.mergeopt(HeadingCell, options.config);
347 347 TextCell.apply(this, [$.extend({}, options, {config: config})]);
348 348
349 349 this.level = 1;
350 350 this.cell_type = 'heading';
351 351 };
352 352
353 353 HeadingCell.options_default = {
354 354 placeholder: "Type Heading Here"
355 355 };
356 356
357 357 HeadingCell.prototype = new TextCell();
358 358
359 359 /** @method fromJSON */
360 360 HeadingCell.prototype.fromJSON = function (data) {
361 361 if (data.level !== undefined){
362 362 this.level = data.level;
363 363 }
364 364 TextCell.prototype.fromJSON.apply(this, arguments);
365 365 };
366 366
367 367
368 368 /** @method toJSON */
369 369 HeadingCell.prototype.toJSON = function () {
370 370 var data = TextCell.prototype.toJSON.apply(this);
371 371 data.level = this.get_level();
372 372 return data;
373 373 };
374 374
375 375 /**
376 376 * can the cell be split into two cells
377 377 * @method is_splittable
378 378 **/
379 379 HeadingCell.prototype.is_splittable = function () {
380 380 return false;
381 381 };
382 382
383 383
384 384 /**
385 385 * can the cell be merged with other cells
386 386 * @method is_mergeable
387 387 **/
388 388 HeadingCell.prototype.is_mergeable = function () {
389 389 return false;
390 390 };
391 391
392 392 /**
393 393 * Change heading level of cell, and re-render
394 394 * @method set_level
395 395 */
396 396 HeadingCell.prototype.set_level = function (level) {
397 397 this.level = level;
398 398 if (this.rendered) {
399 399 this.rendered = false;
400 400 this.render();
401 401 }
402 402 };
403 403
404 404 /** The depth of header cell, based on html (h1 to h6)
405 405 * @method get_level
406 406 * @return {integer} level - for 1 to 6
407 407 */
408 408 HeadingCell.prototype.get_level = function () {
409 409 return this.level;
410 410 };
411 411
412 412
413 413 HeadingCell.prototype.get_rendered = function () {
414 414 var r = this.element.find("div.text_cell_render");
415 415 return r.children().first().html();
416 416 };
417 417
418 418 HeadingCell.prototype.render = function () {
419 419 var cont = TextCell.prototype.render.apply(this);
420 420 if (cont) {
421 421 var text = this.get_text();
422 422 var math = null;
423 423 // Markdown headings must be a single line
424 424 text = text.replace(/\n/g, ' ');
425 425 if (text === "") { text = this.placeholder; }
426 426 text = new Array(this.level + 1).join("#") + " " + text;
427 427 var text_and_math = mathjaxutils.remove_math(text);
428 428 text = text_and_math[0];
429 429 math = text_and_math[1];
430 430 var html = marked.parser(marked.lexer(text));
431 431 html = mathjaxutils.replace_math(html, math);
432 432 html = security.sanitize_html(html);
433 433 var h = $($.parseHTML(html));
434 434 // add id and linkback anchor
435 435 var hash = h.text().replace(/ /g, '-');
436 436 h.attr('id', hash);
437 437 h.append(
438 438 $('<a/>')
439 439 .addClass('anchor-link')
440 440 .attr('href', '#' + hash)
441 441 .text('ΒΆ')
442 442 );
443 443 this.set_rendered(h);
444 444 this.element.find('div.input_area').hide();
445 445 this.element.find("div.text_cell_render").show();
446 446 this.typeset();
447 447 }
448 448 return cont;
449 449 };
450 450
451 451 // Backwards compatability.
452 452 IPython.TextCell = TextCell;
453 453 IPython.MarkdownCell = MarkdownCell;
454 454 IPython.RawCell = RawCell;
455 455 IPython.HeadingCell = HeadingCell;
456 456
457 457 var textcell = {
458 458 'TextCell': TextCell,
459 459 'MarkdownCell': MarkdownCell,
460 460 'RawCell': RawCell,
461 461 'HeadingCell': HeadingCell,
462 462 };
463 463 return textcell;
464 464 });
@@ -1,64 +1,63 b''
1 1 div.cell {
2 2 border: 1px solid transparent;
3 3 .vbox();
4 4 .corner-all();
5 .border-box-sizing();
5 6 border-width: thin;
6 7 border-style: solid;
7
8
8 9 &.selected {
9 10 border-color: @border_color;
10 11 }
11
12
12 13 &.edit_mode {
13 14 border-color: green;
14 15 }
15 }
16 16
17 div.cell {
18 17 width: 100%;
19 18 padding: 5px 5px 5px 0px;
20 19 /* This acts as a spacer between cells, that is outside the border */
21 20 margin: 0px;
22 21 outline: none;
23 22 }
24 23
25 24 div.prompt {
26 25 /* This needs to be wide enough for 3 digit prompt numbers: In[100]: */
27 26 min-width: 15ex;
28 27 /* This padding is tuned to match the padding on the CodeMirror editor. */
29 28 padding: @code_padding;
30 29 margin: 0px;
31 30 font-family: @font-family-monospace;
32 31 text-align: right;
33 32 /* This has to match that of the the CodeMirror class line-height below */
34 33 line-height: @code_line_height;
35 34 }
36 35
37 36 @media (max-width: 480px) {
38 37 // prompts are in the main column on small screens,
39 38 // so text should be left-aligned
40 39 div.prompt {
41 40 text-align: left;
42 41 }
43 42 }
44 43
45 44 div.inner_cell {
46 45 .vbox();
47 46 .box-flex1();
48 47 }
49 48
50 49 /* input_area and input_prompt must match in top border and margin for alignment */
51 50 div.input_area {
52 51 border: 1px solid @light_border_color;
53 52 .corner-all;
54 53 background: @cell_background;
55 54 line-height: @code_line_height;
56 55 }
57 56
58 57 /* This is needed so that empty prompt areas can collapse to zero height when there
59 58 is no content in the output_subarea and the prompt. The main purpose of this is
60 59 to make sure that empty JavaScript output_subareas have no height. */
61 60 div.prompt:empty {
62 61 padding-top: 0;
63 62 padding-bottom: 0;
64 63 }
General Comments 0
You need to be logged in to leave comments. Login now