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