##// END OF EJS Templates
Merge pull request #6699 from Carreau/low-overhead...
Min RK -
r18387:66f3e7c9 merge
parent child Browse files
Show More
@@ -1,532 +1,532
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 *
4 *
5 *
5 *
6 * @module codecell
6 * @module codecell
7 * @namespace codecell
7 * @namespace codecell
8 * @class CodeCell
8 * @class CodeCell
9 */
9 */
10
10
11
11
12 define([
12 define([
13 'base/js/namespace',
13 'base/js/namespace',
14 'jquery',
14 'jquery',
15 'base/js/utils',
15 'base/js/utils',
16 'base/js/keyboard',
16 'base/js/keyboard',
17 'notebook/js/cell',
17 'notebook/js/cell',
18 'notebook/js/outputarea',
18 'notebook/js/outputarea',
19 'notebook/js/completer',
19 'notebook/js/completer',
20 'notebook/js/celltoolbar',
20 'notebook/js/celltoolbar',
21 'codemirror/lib/codemirror',
21 'codemirror/lib/codemirror',
22 'codemirror/mode/python/python',
22 'codemirror/mode/python/python',
23 'notebook/js/codemirror-ipython'
23 'notebook/js/codemirror-ipython'
24 ], function(IPython, $, utils, keyboard, cell, outputarea, completer, celltoolbar, CodeMirror, cmpython, cmip) {
24 ], function(IPython, $, utils, keyboard, cell, outputarea, completer, celltoolbar, CodeMirror, cmpython, cmip) {
25 "use strict";
25 "use strict";
26 var Cell = cell.Cell;
26 var Cell = cell.Cell;
27
27
28 /* local util for codemirror */
28 /* local util for codemirror */
29 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
29 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;};
30
30
31 /**
31 /**
32 *
32 *
33 * function to delete until previous non blanking space character
33 * function to delete until previous non blanking space character
34 * or first multiple of 4 tabstop.
34 * or first multiple of 4 tabstop.
35 * @private
35 * @private
36 */
36 */
37 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
37 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
38 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
38 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
39 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
39 if (!posEq(from, to)) { cm.replaceRange("", from, to); return; }
40 var cur = cm.getCursor(), line = cm.getLine(cur.line);
40 var cur = cm.getCursor(), line = cm.getLine(cur.line);
41 var tabsize = cm.getOption('tabSize');
41 var tabsize = cm.getOption('tabSize');
42 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
42 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
43 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
43 from = {ch:cur.ch-chToPrevTabStop,line:cur.line};
44 var select = cm.getRange(from,cur);
44 var select = cm.getRange(from,cur);
45 if( select.match(/^\ +$/) !== null){
45 if( select.match(/^\ +$/) !== null){
46 cm.replaceRange("",from,cur);
46 cm.replaceRange("",from,cur);
47 } else {
47 } else {
48 cm.deleteH(-1,"char");
48 cm.deleteH(-1,"char");
49 }
49 }
50 };
50 };
51
51
52 var keycodes = keyboard.keycodes;
52 var keycodes = keyboard.keycodes;
53
53
54 var CodeCell = function (kernel, options) {
54 var CodeCell = function (kernel, options) {
55 // Constructor
55 // Constructor
56 //
56 //
57 // A Cell conceived to write code.
57 // A Cell conceived to write code.
58 //
58 //
59 // Parameters:
59 // Parameters:
60 // kernel: Kernel instance
60 // kernel: Kernel instance
61 // The kernel doesn't have to be set at creation time, in that case
61 // The kernel doesn't have to be set at creation time, in that case
62 // it will be null and set_kernel has to be called later.
62 // it will be null and set_kernel has to be called later.
63 // options: dictionary
63 // options: dictionary
64 // Dictionary of keyword arguments.
64 // Dictionary of keyword arguments.
65 // events: $(Events) instance
65 // events: $(Events) instance
66 // config: dictionary
66 // config: dictionary
67 // keyboard_manager: KeyboardManager instance
67 // keyboard_manager: KeyboardManager instance
68 // notebook: Notebook instance
68 // notebook: Notebook instance
69 // tooltip: Tooltip instance
69 // tooltip: Tooltip instance
70 this.kernel = kernel || null;
70 this.kernel = kernel || null;
71 this.notebook = options.notebook;
71 this.notebook = options.notebook;
72 this.collapsed = false;
72 this.collapsed = false;
73 this.events = options.events;
73 this.events = options.events;
74 this.tooltip = options.tooltip;
74 this.tooltip = options.tooltip;
75 this.config = options.config;
75 this.config = options.config;
76
76
77 // create all attributed in constructor function
77 // create all attributed in constructor function
78 // even if null for V8 VM optimisation
78 // even if null for V8 VM optimisation
79 this.input_prompt_number = null;
79 this.input_prompt_number = null;
80 this.celltoolbar = null;
80 this.celltoolbar = null;
81 this.output_area = null;
81 this.output_area = null;
82 this.last_msg_id = null;
82 this.last_msg_id = null;
83 this.completer = null;
83 this.completer = null;
84
84
85
85
86 var config = utils.mergeopt(CodeCell, this.config);
86 var config = utils.mergeopt(CodeCell, this.config);
87 Cell.apply(this,[{
87 Cell.apply(this,[{
88 config: config,
88 config: config,
89 keyboard_manager: options.keyboard_manager,
89 keyboard_manager: options.keyboard_manager,
90 events: this.events}]);
90 events: this.events}]);
91
91
92 // Attributes we want to override in this subclass.
92 // Attributes we want to override in this subclass.
93 this.cell_type = "code";
93 this.cell_type = "code";
94
94
95 var that = this;
95 var that = this;
96 this.element.focusout(
96 this.element.focusout(
97 function() { that.auto_highlight(); }
97 function() { that.auto_highlight(); }
98 );
98 );
99 };
99 };
100
100
101 CodeCell.options_default = {
101 CodeCell.options_default = {
102 cm_config : {
102 cm_config : {
103 extraKeys: {
103 extraKeys: {
104 "Tab" : "indentMore",
104 "Tab" : "indentMore",
105 "Shift-Tab" : "indentLess",
105 "Shift-Tab" : "indentLess",
106 "Backspace" : "delSpaceToPrevTabStop",
106 "Backspace" : "delSpaceToPrevTabStop",
107 "Cmd-/" : "toggleComment",
107 "Cmd-/" : "toggleComment",
108 "Ctrl-/" : "toggleComment"
108 "Ctrl-/" : "toggleComment"
109 },
109 },
110 mode: 'ipython',
110 mode: 'ipython',
111 theme: 'ipython',
111 theme: 'ipython',
112 matchBrackets: true
112 matchBrackets: true
113 }
113 }
114 };
114 };
115
115
116 CodeCell.msg_cells = {};
116 CodeCell.msg_cells = {};
117
117
118 CodeCell.prototype = new Cell();
118 CodeCell.prototype = Object.create(Cell.prototype);
119
119
120 /**
120 /**
121 * @method auto_highlight
121 * @method auto_highlight
122 */
122 */
123 CodeCell.prototype.auto_highlight = function () {
123 CodeCell.prototype.auto_highlight = function () {
124 this._auto_highlight(this.config.cell_magic_highlight);
124 this._auto_highlight(this.config.cell_magic_highlight);
125 };
125 };
126
126
127 /** @method create_element */
127 /** @method create_element */
128 CodeCell.prototype.create_element = function () {
128 CodeCell.prototype.create_element = function () {
129 Cell.prototype.create_element.apply(this, arguments);
129 Cell.prototype.create_element.apply(this, arguments);
130
130
131 var cell = $('<div></div>').addClass('cell code_cell');
131 var cell = $('<div></div>').addClass('cell code_cell');
132 cell.attr('tabindex','2');
132 cell.attr('tabindex','2');
133
133
134 var input = $('<div></div>').addClass('input');
134 var input = $('<div></div>').addClass('input');
135 var prompt = $('<div/>').addClass('prompt input_prompt');
135 var prompt = $('<div/>').addClass('prompt input_prompt');
136 var inner_cell = $('<div/>').addClass('inner_cell');
136 var inner_cell = $('<div/>').addClass('inner_cell');
137 this.celltoolbar = new celltoolbar.CellToolbar({
137 this.celltoolbar = new celltoolbar.CellToolbar({
138 cell: this,
138 cell: this,
139 notebook: this.notebook});
139 notebook: this.notebook});
140 inner_cell.append(this.celltoolbar.element);
140 inner_cell.append(this.celltoolbar.element);
141 var input_area = $('<div/>').addClass('input_area');
141 var input_area = $('<div/>').addClass('input_area');
142 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
142 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
143 this.code_mirror.on('keydown', $.proxy(this.handle_keyevent,this))
143 this.code_mirror.on('keydown', $.proxy(this.handle_keyevent,this))
144 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
144 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
145 inner_cell.append(input_area);
145 inner_cell.append(input_area);
146 input.append(prompt).append(inner_cell);
146 input.append(prompt).append(inner_cell);
147
147
148 var widget_area = $('<div/>')
148 var widget_area = $('<div/>')
149 .addClass('widget-area')
149 .addClass('widget-area')
150 .hide();
150 .hide();
151 this.widget_area = widget_area;
151 this.widget_area = widget_area;
152 var widget_prompt = $('<div/>')
152 var widget_prompt = $('<div/>')
153 .addClass('prompt')
153 .addClass('prompt')
154 .appendTo(widget_area);
154 .appendTo(widget_area);
155 var widget_subarea = $('<div/>')
155 var widget_subarea = $('<div/>')
156 .addClass('widget-subarea')
156 .addClass('widget-subarea')
157 .appendTo(widget_area);
157 .appendTo(widget_area);
158 this.widget_subarea = widget_subarea;
158 this.widget_subarea = widget_subarea;
159 var widget_clear_buton = $('<button />')
159 var widget_clear_buton = $('<button />')
160 .addClass('close')
160 .addClass('close')
161 .html('&times;')
161 .html('&times;')
162 .click(function() {
162 .click(function() {
163 widget_area.slideUp('', function(){ widget_subarea.html(''); });
163 widget_area.slideUp('', function(){ widget_subarea.html(''); });
164 })
164 })
165 .appendTo(widget_prompt);
165 .appendTo(widget_prompt);
166
166
167 var output = $('<div></div>');
167 var output = $('<div></div>');
168 cell.append(input).append(widget_area).append(output);
168 cell.append(input).append(widget_area).append(output);
169 this.element = cell;
169 this.element = cell;
170 this.output_area = new outputarea.OutputArea({
170 this.output_area = new outputarea.OutputArea({
171 selector: output,
171 selector: output,
172 prompt_area: true,
172 prompt_area: true,
173 events: this.events,
173 events: this.events,
174 keyboard_manager: this.keyboard_manager});
174 keyboard_manager: this.keyboard_manager});
175 this.completer = new completer.Completer(this, this.events);
175 this.completer = new completer.Completer(this, this.events);
176 };
176 };
177
177
178 /** @method bind_events */
178 /** @method bind_events */
179 CodeCell.prototype.bind_events = function () {
179 CodeCell.prototype.bind_events = function () {
180 Cell.prototype.bind_events.apply(this);
180 Cell.prototype.bind_events.apply(this);
181 var that = this;
181 var that = this;
182
182
183 this.element.focusout(
183 this.element.focusout(
184 function() { that.auto_highlight(); }
184 function() { that.auto_highlight(); }
185 );
185 );
186 };
186 };
187
187
188
188
189 /**
189 /**
190 * This method gets called in CodeMirror's onKeyDown/onKeyPress
190 * This method gets called in CodeMirror's onKeyDown/onKeyPress
191 * handlers and is used to provide custom key handling. Its return
191 * handlers and is used to provide custom key handling. Its return
192 * value is used to determine if CodeMirror should ignore the event:
192 * value is used to determine if CodeMirror should ignore the event:
193 * true = ignore, false = don't ignore.
193 * true = ignore, false = don't ignore.
194 * @method handle_codemirror_keyevent
194 * @method handle_codemirror_keyevent
195 */
195 */
196 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
196 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
197
197
198 var that = this;
198 var that = this;
199 // whatever key is pressed, first, cancel the tooltip request before
199 // whatever key is pressed, first, cancel the tooltip request before
200 // they are sent, and remove tooltip if any, except for tab again
200 // they are sent, and remove tooltip if any, except for tab again
201 var tooltip_closed = null;
201 var tooltip_closed = null;
202 if (event.type === 'keydown' && event.which != keycodes.tab ) {
202 if (event.type === 'keydown' && event.which != keycodes.tab ) {
203 tooltip_closed = this.tooltip.remove_and_cancel_tooltip();
203 tooltip_closed = this.tooltip.remove_and_cancel_tooltip();
204 }
204 }
205
205
206 var cur = editor.getCursor();
206 var cur = editor.getCursor();
207 if (event.keyCode === keycodes.enter){
207 if (event.keyCode === keycodes.enter){
208 this.auto_highlight();
208 this.auto_highlight();
209 }
209 }
210
210
211 if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) {
211 if (event.which === keycodes.down && event.type === 'keypress' && this.tooltip.time_before_tooltip >= 0) {
212 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
212 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
213 // browser and keyboard layout !
213 // browser and keyboard layout !
214 // Pressing '(' , request tooltip, don't forget to reappend it
214 // Pressing '(' , request tooltip, don't forget to reappend it
215 // The second argument says to hide the tooltip if the docstring
215 // The second argument says to hide the tooltip if the docstring
216 // is actually empty
216 // is actually empty
217 this.tooltip.pending(that, true);
217 this.tooltip.pending(that, true);
218 } else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
218 } else if ( tooltip_closed && event.which === keycodes.esc && event.type === 'keydown') {
219 // If tooltip is active, cancel it. The call to
219 // If tooltip is active, cancel it. The call to
220 // remove_and_cancel_tooltip above doesn't pass, force=true.
220 // remove_and_cancel_tooltip above doesn't pass, force=true.
221 // Because of this it won't actually close the tooltip
221 // Because of this it won't actually close the tooltip
222 // if it is in sticky mode. Thus, we have to check again if it is open
222 // if it is in sticky mode. Thus, we have to check again if it is open
223 // and close it with force=true.
223 // and close it with force=true.
224 if (!this.tooltip._hidden) {
224 if (!this.tooltip._hidden) {
225 this.tooltip.remove_and_cancel_tooltip(true);
225 this.tooltip.remove_and_cancel_tooltip(true);
226 }
226 }
227 // If we closed the tooltip, don't let CM or the global handlers
227 // If we closed the tooltip, don't let CM or the global handlers
228 // handle this event.
228 // handle this event.
229 event.codemirrorIgnore = true;
229 event.codemirrorIgnore = true;
230 event.preventDefault();
230 event.preventDefault();
231 return true;
231 return true;
232 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
232 } else if (event.keyCode === keycodes.tab && event.type === 'keydown' && event.shiftKey) {
233 if (editor.somethingSelected() || editor.getSelections().length !== 1){
233 if (editor.somethingSelected() || editor.getSelections().length !== 1){
234 var anchor = editor.getCursor("anchor");
234 var anchor = editor.getCursor("anchor");
235 var head = editor.getCursor("head");
235 var head = editor.getCursor("head");
236 if( anchor.line != head.line){
236 if( anchor.line != head.line){
237 return false;
237 return false;
238 }
238 }
239 }
239 }
240 this.tooltip.request(that);
240 this.tooltip.request(that);
241 event.codemirrorIgnore = true;
241 event.codemirrorIgnore = true;
242 event.preventDefault();
242 event.preventDefault();
243 return true;
243 return true;
244 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
244 } else if (event.keyCode === keycodes.tab && event.type == 'keydown') {
245 // Tab completion.
245 // Tab completion.
246 this.tooltip.remove_and_cancel_tooltip();
246 this.tooltip.remove_and_cancel_tooltip();
247
247
248 // completion does not work on multicursor, it might be possible though in some cases
248 // completion does not work on multicursor, it might be possible though in some cases
249 if (editor.somethingSelected() || editor.getSelections().length > 1) {
249 if (editor.somethingSelected() || editor.getSelections().length > 1) {
250 return false;
250 return false;
251 }
251 }
252 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
252 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
253 if (pre_cursor.trim() === "") {
253 if (pre_cursor.trim() === "") {
254 // Don't autocomplete if the part of the line before the cursor
254 // Don't autocomplete if the part of the line before the cursor
255 // is empty. In this case, let CodeMirror handle indentation.
255 // is empty. In this case, let CodeMirror handle indentation.
256 return false;
256 return false;
257 } else {
257 } else {
258 event.codemirrorIgnore = true;
258 event.codemirrorIgnore = true;
259 event.preventDefault();
259 event.preventDefault();
260 this.completer.startCompletion();
260 this.completer.startCompletion();
261 return true;
261 return true;
262 }
262 }
263 }
263 }
264
264
265 // keyboard event wasn't one of those unique to code cells, let's see
265 // keyboard event wasn't one of those unique to code cells, let's see
266 // if it's one of the generic ones (i.e. check edit mode shortcuts)
266 // if it's one of the generic ones (i.e. check edit mode shortcuts)
267 return Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
267 return Cell.prototype.handle_codemirror_keyevent.apply(this, [editor, event]);
268 };
268 };
269
269
270 // Kernel related calls.
270 // Kernel related calls.
271
271
272 CodeCell.prototype.set_kernel = function (kernel) {
272 CodeCell.prototype.set_kernel = function (kernel) {
273 this.kernel = kernel;
273 this.kernel = kernel;
274 };
274 };
275
275
276 /**
276 /**
277 * Execute current code cell to the kernel
277 * Execute current code cell to the kernel
278 * @method execute
278 * @method execute
279 */
279 */
280 CodeCell.prototype.execute = function () {
280 CodeCell.prototype.execute = function () {
281 this.output_area.clear_output();
281 this.output_area.clear_output();
282
282
283 // Clear widget area
283 // Clear widget area
284 this.widget_subarea.html('');
284 this.widget_subarea.html('');
285 this.widget_subarea.height('');
285 this.widget_subarea.height('');
286 this.widget_area.height('');
286 this.widget_area.height('');
287 this.widget_area.hide();
287 this.widget_area.hide();
288
288
289 this.set_input_prompt('*');
289 this.set_input_prompt('*');
290 this.element.addClass("running");
290 this.element.addClass("running");
291 if (this.last_msg_id) {
291 if (this.last_msg_id) {
292 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
292 this.kernel.clear_callbacks_for_msg(this.last_msg_id);
293 }
293 }
294 var callbacks = this.get_callbacks();
294 var callbacks = this.get_callbacks();
295
295
296 var old_msg_id = this.last_msg_id;
296 var old_msg_id = this.last_msg_id;
297 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
297 this.last_msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false, store_history: true});
298 if (old_msg_id) {
298 if (old_msg_id) {
299 delete CodeCell.msg_cells[old_msg_id];
299 delete CodeCell.msg_cells[old_msg_id];
300 }
300 }
301 CodeCell.msg_cells[this.last_msg_id] = this;
301 CodeCell.msg_cells[this.last_msg_id] = this;
302 this.render();
302 this.render();
303 };
303 };
304
304
305 /**
305 /**
306 * Construct the default callbacks for
306 * Construct the default callbacks for
307 * @method get_callbacks
307 * @method get_callbacks
308 */
308 */
309 CodeCell.prototype.get_callbacks = function () {
309 CodeCell.prototype.get_callbacks = function () {
310 return {
310 return {
311 shell : {
311 shell : {
312 reply : $.proxy(this._handle_execute_reply, this),
312 reply : $.proxy(this._handle_execute_reply, this),
313 payload : {
313 payload : {
314 set_next_input : $.proxy(this._handle_set_next_input, this),
314 set_next_input : $.proxy(this._handle_set_next_input, this),
315 page : $.proxy(this._open_with_pager, this)
315 page : $.proxy(this._open_with_pager, this)
316 }
316 }
317 },
317 },
318 iopub : {
318 iopub : {
319 output : $.proxy(this.output_area.handle_output, this.output_area),
319 output : $.proxy(this.output_area.handle_output, this.output_area),
320 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
320 clear_output : $.proxy(this.output_area.handle_clear_output, this.output_area),
321 },
321 },
322 input : $.proxy(this._handle_input_request, this)
322 input : $.proxy(this._handle_input_request, this)
323 };
323 };
324 };
324 };
325
325
326 CodeCell.prototype._open_with_pager = function (payload) {
326 CodeCell.prototype._open_with_pager = function (payload) {
327 this.events.trigger('open_with_text.Pager', payload);
327 this.events.trigger('open_with_text.Pager', payload);
328 };
328 };
329
329
330 /**
330 /**
331 * @method _handle_execute_reply
331 * @method _handle_execute_reply
332 * @private
332 * @private
333 */
333 */
334 CodeCell.prototype._handle_execute_reply = function (msg) {
334 CodeCell.prototype._handle_execute_reply = function (msg) {
335 this.set_input_prompt(msg.content.execution_count);
335 this.set_input_prompt(msg.content.execution_count);
336 this.element.removeClass("running");
336 this.element.removeClass("running");
337 this.events.trigger('set_dirty.Notebook', {value: true});
337 this.events.trigger('set_dirty.Notebook', {value: true});
338 };
338 };
339
339
340 /**
340 /**
341 * @method _handle_set_next_input
341 * @method _handle_set_next_input
342 * @private
342 * @private
343 */
343 */
344 CodeCell.prototype._handle_set_next_input = function (payload) {
344 CodeCell.prototype._handle_set_next_input = function (payload) {
345 var data = {'cell': this, 'text': payload.text};
345 var data = {'cell': this, 'text': payload.text};
346 this.events.trigger('set_next_input.Notebook', data);
346 this.events.trigger('set_next_input.Notebook', data);
347 };
347 };
348
348
349 /**
349 /**
350 * @method _handle_input_request
350 * @method _handle_input_request
351 * @private
351 * @private
352 */
352 */
353 CodeCell.prototype._handle_input_request = function (msg) {
353 CodeCell.prototype._handle_input_request = function (msg) {
354 this.output_area.append_raw_input(msg);
354 this.output_area.append_raw_input(msg);
355 };
355 };
356
356
357
357
358 // Basic cell manipulation.
358 // Basic cell manipulation.
359
359
360 CodeCell.prototype.select = function () {
360 CodeCell.prototype.select = function () {
361 var cont = Cell.prototype.select.apply(this);
361 var cont = Cell.prototype.select.apply(this);
362 if (cont) {
362 if (cont) {
363 this.code_mirror.refresh();
363 this.code_mirror.refresh();
364 this.auto_highlight();
364 this.auto_highlight();
365 }
365 }
366 return cont;
366 return cont;
367 };
367 };
368
368
369 CodeCell.prototype.render = function () {
369 CodeCell.prototype.render = function () {
370 var cont = Cell.prototype.render.apply(this);
370 var cont = Cell.prototype.render.apply(this);
371 // Always execute, even if we are already in the rendered state
371 // Always execute, even if we are already in the rendered state
372 return cont;
372 return cont;
373 };
373 };
374
374
375 CodeCell.prototype.select_all = function () {
375 CodeCell.prototype.select_all = function () {
376 var start = {line: 0, ch: 0};
376 var start = {line: 0, ch: 0};
377 var nlines = this.code_mirror.lineCount();
377 var nlines = this.code_mirror.lineCount();
378 var last_line = this.code_mirror.getLine(nlines-1);
378 var last_line = this.code_mirror.getLine(nlines-1);
379 var end = {line: nlines-1, ch: last_line.length};
379 var end = {line: nlines-1, ch: last_line.length};
380 this.code_mirror.setSelection(start, end);
380 this.code_mirror.setSelection(start, end);
381 };
381 };
382
382
383
383
384 CodeCell.prototype.collapse_output = function () {
384 CodeCell.prototype.collapse_output = function () {
385 this.collapsed = true;
385 this.collapsed = true;
386 this.output_area.collapse();
386 this.output_area.collapse();
387 };
387 };
388
388
389
389
390 CodeCell.prototype.expand_output = function () {
390 CodeCell.prototype.expand_output = function () {
391 this.collapsed = false;
391 this.collapsed = false;
392 this.output_area.expand();
392 this.output_area.expand();
393 this.output_area.unscroll_area();
393 this.output_area.unscroll_area();
394 };
394 };
395
395
396 CodeCell.prototype.scroll_output = function () {
396 CodeCell.prototype.scroll_output = function () {
397 this.output_area.expand();
397 this.output_area.expand();
398 this.output_area.scroll_if_long();
398 this.output_area.scroll_if_long();
399 };
399 };
400
400
401 CodeCell.prototype.toggle_output = function () {
401 CodeCell.prototype.toggle_output = function () {
402 this.collapsed = Boolean(1 - this.collapsed);
402 this.collapsed = Boolean(1 - this.collapsed);
403 this.output_area.toggle_output();
403 this.output_area.toggle_output();
404 };
404 };
405
405
406 CodeCell.prototype.toggle_output_scroll = function () {
406 CodeCell.prototype.toggle_output_scroll = function () {
407 this.output_area.toggle_scroll();
407 this.output_area.toggle_scroll();
408 };
408 };
409
409
410
410
411 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
411 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
412 var ns;
412 var ns;
413 if (prompt_value === undefined || prompt_value === null) {
413 if (prompt_value === undefined || prompt_value === null) {
414 ns = "&nbsp;";
414 ns = "&nbsp;";
415 } else {
415 } else {
416 ns = encodeURIComponent(prompt_value);
416 ns = encodeURIComponent(prompt_value);
417 }
417 }
418 return 'In&nbsp;[' + ns + ']:';
418 return 'In&nbsp;[' + ns + ']:';
419 };
419 };
420
420
421 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
421 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
422 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
422 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
423 for(var i=1; i < lines_number; i++) {
423 for(var i=1; i < lines_number; i++) {
424 html.push(['...:']);
424 html.push(['...:']);
425 }
425 }
426 return html.join('<br/>');
426 return html.join('<br/>');
427 };
427 };
428
428
429 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
429 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
430
430
431
431
432 CodeCell.prototype.set_input_prompt = function (number) {
432 CodeCell.prototype.set_input_prompt = function (number) {
433 var nline = 1;
433 var nline = 1;
434 if (this.code_mirror !== undefined) {
434 if (this.code_mirror !== undefined) {
435 nline = this.code_mirror.lineCount();
435 nline = this.code_mirror.lineCount();
436 }
436 }
437 this.input_prompt_number = number;
437 this.input_prompt_number = number;
438 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
438 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
439 // This HTML call is okay because the user contents are escaped.
439 // This HTML call is okay because the user contents are escaped.
440 this.element.find('div.input_prompt').html(prompt_html);
440 this.element.find('div.input_prompt').html(prompt_html);
441 };
441 };
442
442
443
443
444 CodeCell.prototype.clear_input = function () {
444 CodeCell.prototype.clear_input = function () {
445 this.code_mirror.setValue('');
445 this.code_mirror.setValue('');
446 };
446 };
447
447
448
448
449 CodeCell.prototype.get_text = function () {
449 CodeCell.prototype.get_text = function () {
450 return this.code_mirror.getValue();
450 return this.code_mirror.getValue();
451 };
451 };
452
452
453
453
454 CodeCell.prototype.set_text = function (code) {
454 CodeCell.prototype.set_text = function (code) {
455 return this.code_mirror.setValue(code);
455 return this.code_mirror.setValue(code);
456 };
456 };
457
457
458
458
459 CodeCell.prototype.clear_output = function (wait) {
459 CodeCell.prototype.clear_output = function (wait) {
460 this.output_area.clear_output(wait);
460 this.output_area.clear_output(wait);
461 this.set_input_prompt();
461 this.set_input_prompt();
462 };
462 };
463
463
464
464
465 // JSON serialization
465 // JSON serialization
466
466
467 CodeCell.prototype.fromJSON = function (data) {
467 CodeCell.prototype.fromJSON = function (data) {
468 Cell.prototype.fromJSON.apply(this, arguments);
468 Cell.prototype.fromJSON.apply(this, arguments);
469 if (data.cell_type === 'code') {
469 if (data.cell_type === 'code') {
470 if (data.input !== undefined) {
470 if (data.input !== undefined) {
471 this.set_text(data.input);
471 this.set_text(data.input);
472 // make this value the starting point, so that we can only undo
472 // make this value the starting point, so that we can only undo
473 // to this state, instead of a blank cell
473 // to this state, instead of a blank cell
474 this.code_mirror.clearHistory();
474 this.code_mirror.clearHistory();
475 this.auto_highlight();
475 this.auto_highlight();
476 }
476 }
477 if (data.prompt_number !== undefined) {
477 if (data.prompt_number !== undefined) {
478 this.set_input_prompt(data.prompt_number);
478 this.set_input_prompt(data.prompt_number);
479 } else {
479 } else {
480 this.set_input_prompt();
480 this.set_input_prompt();
481 }
481 }
482 this.output_area.trusted = data.metadata.trusted || false;
482 this.output_area.trusted = data.metadata.trusted || false;
483 this.output_area.fromJSON(data.outputs);
483 this.output_area.fromJSON(data.outputs);
484 if (data.collapsed !== undefined) {
484 if (data.collapsed !== undefined) {
485 if (data.collapsed) {
485 if (data.collapsed) {
486 this.collapse_output();
486 this.collapse_output();
487 } else {
487 } else {
488 this.expand_output();
488 this.expand_output();
489 }
489 }
490 }
490 }
491 }
491 }
492 };
492 };
493
493
494
494
495 CodeCell.prototype.toJSON = function () {
495 CodeCell.prototype.toJSON = function () {
496 var data = Cell.prototype.toJSON.apply(this);
496 var data = Cell.prototype.toJSON.apply(this);
497 data.input = this.get_text();
497 data.input = this.get_text();
498 // is finite protect against undefined and '*' value
498 // is finite protect against undefined and '*' value
499 if (isFinite(this.input_prompt_number)) {
499 if (isFinite(this.input_prompt_number)) {
500 data.prompt_number = this.input_prompt_number;
500 data.prompt_number = this.input_prompt_number;
501 }
501 }
502 var outputs = this.output_area.toJSON();
502 var outputs = this.output_area.toJSON();
503 data.outputs = outputs;
503 data.outputs = outputs;
504 data.language = 'python';
504 data.language = 'python';
505 data.metadata.trusted = this.output_area.trusted;
505 data.metadata.trusted = this.output_area.trusted;
506 data.collapsed = this.output_area.collapsed;
506 data.collapsed = this.output_area.collapsed;
507 return data;
507 return data;
508 };
508 };
509
509
510 /**
510 /**
511 * handle cell level logic when a cell is unselected
511 * handle cell level logic when a cell is unselected
512 * @method unselect
512 * @method unselect
513 * @return is the action being taken
513 * @return is the action being taken
514 */
514 */
515 CodeCell.prototype.unselect = function () {
515 CodeCell.prototype.unselect = function () {
516 var cont = Cell.prototype.unselect.apply(this);
516 var cont = Cell.prototype.unselect.apply(this);
517 if (cont) {
517 if (cont) {
518 // When a code cell is usnelected, make sure that the corresponding
518 // When a code cell is usnelected, make sure that the corresponding
519 // tooltip and completer to that cell is closed.
519 // tooltip and completer to that cell is closed.
520 this.tooltip.remove_and_cancel_tooltip(true);
520 this.tooltip.remove_and_cancel_tooltip(true);
521 if (this.completer !== null) {
521 if (this.completer !== null) {
522 this.completer.close();
522 this.completer.close();
523 }
523 }
524 }
524 }
525 return cont;
525 return cont;
526 };
526 };
527
527
528 // Backwards compatability.
528 // Backwards compatability.
529 IPython.CodeCell = CodeCell;
529 IPython.CodeCell = CodeCell;
530
530
531 return {'CodeCell': CodeCell};
531 return {'CodeCell': CodeCell};
532 });
532 });
@@ -1,226 +1,226
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/toolbar',
7 'notebook/js/toolbar',
8 'notebook/js/celltoolbar',
8 'notebook/js/celltoolbar',
9 ], function(IPython, $, toolbar, celltoolbar) {
9 ], function(IPython, $, toolbar, celltoolbar) {
10 "use strict";
10 "use strict";
11
11
12 var MainToolBar = function (selector, options) {
12 var MainToolBar = function (selector, options) {
13 // Constructor
13 // Constructor
14 //
14 //
15 // Parameters:
15 // Parameters:
16 // selector: string
16 // selector: string
17 // options: dictionary
17 // options: dictionary
18 // Dictionary of keyword arguments.
18 // Dictionary of keyword arguments.
19 // events: $(Events) instance
19 // events: $(Events) instance
20 // notebook: Notebook instance
20 // notebook: Notebook instance
21 toolbar.ToolBar.apply(this, arguments);
21 toolbar.ToolBar.apply(this, arguments);
22 this.events = options.events;
22 this.events = options.events;
23 this.notebook = options.notebook;
23 this.notebook = options.notebook;
24 this.construct();
24 this.construct();
25 this.add_celltype_list();
25 this.add_celltype_list();
26 this.add_celltoolbar_list();
26 this.add_celltoolbar_list();
27 this.bind_events();
27 this.bind_events();
28 };
28 };
29
29
30 MainToolBar.prototype = new toolbar.ToolBar();
30 MainToolBar.prototype = Object.create(toolbar.ToolBar.prototype);
31
31
32 MainToolBar.prototype.construct = function () {
32 MainToolBar.prototype.construct = function () {
33 var that = this;
33 var that = this;
34 this.add_buttons_group([
34 this.add_buttons_group([
35 {
35 {
36 id : 'save_b',
36 id : 'save_b',
37 label : 'Save and Checkpoint',
37 label : 'Save and Checkpoint',
38 icon : 'fa-save',
38 icon : 'fa-save',
39 callback : function () {
39 callback : function () {
40 that.notebook.save_checkpoint();
40 that.notebook.save_checkpoint();
41 }
41 }
42 }
42 }
43 ]);
43 ]);
44
44
45 this.add_buttons_group([
45 this.add_buttons_group([
46 {
46 {
47 id : 'insert_below_b',
47 id : 'insert_below_b',
48 label : 'Insert Cell Below',
48 label : 'Insert Cell Below',
49 icon : 'fa-plus',
49 icon : 'fa-plus',
50 callback : function () {
50 callback : function () {
51 that.notebook.insert_cell_below('code');
51 that.notebook.insert_cell_below('code');
52 that.notebook.select_next();
52 that.notebook.select_next();
53 that.notebook.focus_cell();
53 that.notebook.focus_cell();
54 }
54 }
55 }
55 }
56 ],'insert_above_below');
56 ],'insert_above_below');
57
57
58 this.add_buttons_group([
58 this.add_buttons_group([
59 {
59 {
60 id : 'cut_b',
60 id : 'cut_b',
61 label : 'Cut Cell',
61 label : 'Cut Cell',
62 icon : 'fa-cut',
62 icon : 'fa-cut',
63 callback : function () {
63 callback : function () {
64 that.notebook.cut_cell();
64 that.notebook.cut_cell();
65 }
65 }
66 },
66 },
67 {
67 {
68 id : 'copy_b',
68 id : 'copy_b',
69 label : 'Copy Cell',
69 label : 'Copy Cell',
70 icon : 'fa-copy',
70 icon : 'fa-copy',
71 callback : function () {
71 callback : function () {
72 that.notebook.copy_cell();
72 that.notebook.copy_cell();
73 }
73 }
74 },
74 },
75 {
75 {
76 id : 'paste_b',
76 id : 'paste_b',
77 label : 'Paste Cell Below',
77 label : 'Paste Cell Below',
78 icon : 'fa-paste',
78 icon : 'fa-paste',
79 callback : function () {
79 callback : function () {
80 that.notebook.paste_cell_below();
80 that.notebook.paste_cell_below();
81 }
81 }
82 }
82 }
83 ],'cut_copy_paste');
83 ],'cut_copy_paste');
84
84
85 this.add_buttons_group([
85 this.add_buttons_group([
86 {
86 {
87 id : 'move_up_b',
87 id : 'move_up_b',
88 label : 'Move Cell Up',
88 label : 'Move Cell Up',
89 icon : 'fa-arrow-up',
89 icon : 'fa-arrow-up',
90 callback : function () {
90 callback : function () {
91 that.notebook.move_cell_up();
91 that.notebook.move_cell_up();
92 }
92 }
93 },
93 },
94 {
94 {
95 id : 'move_down_b',
95 id : 'move_down_b',
96 label : 'Move Cell Down',
96 label : 'Move Cell Down',
97 icon : 'fa-arrow-down',
97 icon : 'fa-arrow-down',
98 callback : function () {
98 callback : function () {
99 that.notebook.move_cell_down();
99 that.notebook.move_cell_down();
100 }
100 }
101 }
101 }
102 ],'move_up_down');
102 ],'move_up_down');
103
103
104
104
105 this.add_buttons_group([
105 this.add_buttons_group([
106 {
106 {
107 id : 'run_b',
107 id : 'run_b',
108 label : 'Run Cell',
108 label : 'Run Cell',
109 icon : 'fa-play',
109 icon : 'fa-play',
110 callback : function () {
110 callback : function () {
111 // emulate default shift-enter behavior
111 // emulate default shift-enter behavior
112 that.notebook.execute_cell_and_select_below();
112 that.notebook.execute_cell_and_select_below();
113 }
113 }
114 },
114 },
115 {
115 {
116 id : 'interrupt_b',
116 id : 'interrupt_b',
117 label : 'Interrupt',
117 label : 'Interrupt',
118 icon : 'fa-stop',
118 icon : 'fa-stop',
119 callback : function () {
119 callback : function () {
120 that.notebook.kernel.interrupt();
120 that.notebook.kernel.interrupt();
121 }
121 }
122 },
122 },
123 {
123 {
124 id : 'repeat_b',
124 id : 'repeat_b',
125 label : 'Restart Kernel',
125 label : 'Restart Kernel',
126 icon : 'fa-repeat',
126 icon : 'fa-repeat',
127 callback : function () {
127 callback : function () {
128 that.notebook.restart_kernel();
128 that.notebook.restart_kernel();
129 }
129 }
130 }
130 }
131 ],'run_int');
131 ],'run_int');
132 };
132 };
133
133
134 MainToolBar.prototype.add_celltype_list = function () {
134 MainToolBar.prototype.add_celltype_list = function () {
135 this.element
135 this.element
136 .append($('<select/>')
136 .append($('<select/>')
137 .attr('id','cell_type')
137 .attr('id','cell_type')
138 .addClass('form-control select-xs')
138 .addClass('form-control select-xs')
139 .append($('<option/>').attr('value','code').text('Code'))
139 .append($('<option/>').attr('value','code').text('Code'))
140 .append($('<option/>').attr('value','markdown').text('Markdown'))
140 .append($('<option/>').attr('value','markdown').text('Markdown'))
141 .append($('<option/>').attr('value','raw').text('Raw NBConvert'))
141 .append($('<option/>').attr('value','raw').text('Raw NBConvert'))
142 .append($('<option/>').attr('value','heading1').text('Heading 1'))
142 .append($('<option/>').attr('value','heading1').text('Heading 1'))
143 .append($('<option/>').attr('value','heading2').text('Heading 2'))
143 .append($('<option/>').attr('value','heading2').text('Heading 2'))
144 .append($('<option/>').attr('value','heading3').text('Heading 3'))
144 .append($('<option/>').attr('value','heading3').text('Heading 3'))
145 .append($('<option/>').attr('value','heading4').text('Heading 4'))
145 .append($('<option/>').attr('value','heading4').text('Heading 4'))
146 .append($('<option/>').attr('value','heading5').text('Heading 5'))
146 .append($('<option/>').attr('value','heading5').text('Heading 5'))
147 .append($('<option/>').attr('value','heading6').text('Heading 6'))
147 .append($('<option/>').attr('value','heading6').text('Heading 6'))
148 );
148 );
149 };
149 };
150
150
151 MainToolBar.prototype.add_celltoolbar_list = function () {
151 MainToolBar.prototype.add_celltoolbar_list = function () {
152 var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:');
152 var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:');
153 var select = $('<select/>')
153 var select = $('<select/>')
154 .attr('id', 'ctb_select')
154 .attr('id', 'ctb_select')
155 .addClass('form-control select-xs')
155 .addClass('form-control select-xs')
156 .append($('<option/>').attr('value', '').text('None'));
156 .append($('<option/>').attr('value', '').text('None'));
157 this.element.append(label).append(select);
157 this.element.append(label).append(select);
158 var that = this;
158 var that = this;
159 select.change(function() {
159 select.change(function() {
160 var val = $(this).val();
160 var val = $(this).val();
161 if (val ==='') {
161 if (val ==='') {
162 celltoolbar.CellToolbar.global_hide();
162 celltoolbar.CellToolbar.global_hide();
163 delete that.notebook.metadata.celltoolbar;
163 delete that.notebook.metadata.celltoolbar;
164 } else {
164 } else {
165 celltoolbar.CellToolbar.global_show();
165 celltoolbar.CellToolbar.global_show();
166 celltoolbar.CellToolbar.activate_preset(val, that.events);
166 celltoolbar.CellToolbar.activate_preset(val, that.events);
167 that.notebook.metadata.celltoolbar = val;
167 that.notebook.metadata.celltoolbar = val;
168 }
168 }
169 });
169 });
170 // Setup the currently registered presets.
170 // Setup the currently registered presets.
171 var presets = celltoolbar.CellToolbar.list_presets();
171 var presets = celltoolbar.CellToolbar.list_presets();
172 for (var i=0; i<presets.length; i++) {
172 for (var i=0; i<presets.length; i++) {
173 var name = presets[i];
173 var name = presets[i];
174 select.append($('<option/>').attr('value', name).text(name));
174 select.append($('<option/>').attr('value', name).text(name));
175 }
175 }
176 // Setup future preset registrations.
176 // Setup future preset registrations.
177 this.events.on('preset_added.CellToolbar', function (event, data) {
177 this.events.on('preset_added.CellToolbar', function (event, data) {
178 var name = data.name;
178 var name = data.name;
179 select.append($('<option/>').attr('value', name).text(name));
179 select.append($('<option/>').attr('value', name).text(name));
180 });
180 });
181 // Update select value when a preset is activated.
181 // Update select value when a preset is activated.
182 this.events.on('preset_activated.CellToolbar', function (event, data) {
182 this.events.on('preset_activated.CellToolbar', function (event, data) {
183 if (select.val() !== data.name)
183 if (select.val() !== data.name)
184 select.val(data.name);
184 select.val(data.name);
185 });
185 });
186 };
186 };
187
187
188 MainToolBar.prototype.bind_events = function () {
188 MainToolBar.prototype.bind_events = function () {
189 var that = this;
189 var that = this;
190
190
191 this.element.find('#cell_type').change(function () {
191 this.element.find('#cell_type').change(function () {
192 var cell_type = $(this).val();
192 var cell_type = $(this).val();
193 if (cell_type === 'code') {
193 if (cell_type === 'code') {
194 that.notebook.to_code();
194 that.notebook.to_code();
195 } else if (cell_type === 'markdown') {
195 } else if (cell_type === 'markdown') {
196 that.notebook.to_markdown();
196 that.notebook.to_markdown();
197 } else if (cell_type === 'raw') {
197 } else if (cell_type === 'raw') {
198 that.notebook.to_raw();
198 that.notebook.to_raw();
199 } else if (cell_type === 'heading1') {
199 } else if (cell_type === 'heading1') {
200 that.notebook.to_heading(undefined, 1);
200 that.notebook.to_heading(undefined, 1);
201 } else if (cell_type === 'heading2') {
201 } else if (cell_type === 'heading2') {
202 that.notebook.to_heading(undefined, 2);
202 that.notebook.to_heading(undefined, 2);
203 } else if (cell_type === 'heading3') {
203 } else if (cell_type === 'heading3') {
204 that.notebook.to_heading(undefined, 3);
204 that.notebook.to_heading(undefined, 3);
205 } else if (cell_type === 'heading4') {
205 } else if (cell_type === 'heading4') {
206 that.notebook.to_heading(undefined, 4);
206 that.notebook.to_heading(undefined, 4);
207 } else if (cell_type === 'heading5') {
207 } else if (cell_type === 'heading5') {
208 that.notebook.to_heading(undefined, 5);
208 that.notebook.to_heading(undefined, 5);
209 } else if (cell_type === 'heading6') {
209 } else if (cell_type === 'heading6') {
210 that.notebook.to_heading(undefined, 6);
210 that.notebook.to_heading(undefined, 6);
211 }
211 }
212 });
212 });
213 this.events.on('selected_cell_type_changed.Notebook', function (event, data) {
213 this.events.on('selected_cell_type_changed.Notebook', function (event, data) {
214 if (data.cell_type === 'heading') {
214 if (data.cell_type === 'heading') {
215 that.element.find('#cell_type').val(data.cell_type+data.level);
215 that.element.find('#cell_type').val(data.cell_type+data.level);
216 } else {
216 } else {
217 that.element.find('#cell_type').val(data.cell_type);
217 that.element.find('#cell_type').val(data.cell_type);
218 }
218 }
219 });
219 });
220 };
220 };
221
221
222 // Backwards compatability.
222 // Backwards compatability.
223 IPython.MainToolBar = MainToolBar;
223 IPython.MainToolBar = MainToolBar;
224
224
225 return {'MainToolBar': MainToolBar};
225 return {'MainToolBar': MainToolBar};
226 });
226 });
@@ -1,193 +1,193
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 define(['jquery'], function($){
3 define(['jquery'], function($){
4 "use strict";
4 "use strict";
5
5
6 var ScrollManager = function(notebook, options) {
6 var ScrollManager = function(notebook, options) {
7 // Public constructor.
7 // Public constructor.
8 this.notebook = notebook;
8 this.notebook = notebook;
9 options = options || {};
9 options = options || {};
10 this.animation_speed = options.animation_speed || 250; //ms
10 this.animation_speed = options.animation_speed || 250; //ms
11 };
11 };
12
12
13 ScrollManager.prototype.scroll = function (delta) {
13 ScrollManager.prototype.scroll = function (delta) {
14 // Scroll the document.
14 // Scroll the document.
15 //
15 //
16 // Parameters
16 // Parameters
17 // ----------
17 // ----------
18 // delta: integer
18 // delta: integer
19 // direction to scroll the document. Positive is downwards.
19 // direction to scroll the document. Positive is downwards.
20 // Unit is one page length.
20 // Unit is one page length.
21 this.scroll_some(delta);
21 this.scroll_some(delta);
22 return false;
22 return false;
23 };
23 };
24
24
25 ScrollManager.prototype.scroll_to = function(selector) {
25 ScrollManager.prototype.scroll_to = function(selector) {
26 // Scroll to an element in the notebook.
26 // Scroll to an element in the notebook.
27 $('#notebook').animate({'scrollTop': $(selector).offset().top + $('#notebook').scrollTop() - $('#notebook').offset().top}, this.animation_speed);
27 $('#notebook').animate({'scrollTop': $(selector).offset().top + $('#notebook').scrollTop() - $('#notebook').offset().top}, this.animation_speed);
28 };
28 };
29
29
30 ScrollManager.prototype.scroll_some = function(pages) {
30 ScrollManager.prototype.scroll_some = function(pages) {
31 // Scroll up or down a given number of pages.
31 // Scroll up or down a given number of pages.
32 //
32 //
33 // Parameters
33 // Parameters
34 // ----------
34 // ----------
35 // pages: integer
35 // pages: integer
36 // number of pages to scroll the document, may be positive or negative.
36 // number of pages to scroll the document, may be positive or negative.
37 $('#notebook').animate({'scrollTop': $('#notebook').scrollTop() + pages * $('#notebook').height()}, this.animation_speed);
37 $('#notebook').animate({'scrollTop': $('#notebook').scrollTop() + pages * $('#notebook').height()}, this.animation_speed);
38 };
38 };
39
39
40 ScrollManager.prototype.get_first_visible_cell = function() {
40 ScrollManager.prototype.get_first_visible_cell = function() {
41 // Gets the index of the first visible cell in the document.
41 // Gets the index of the first visible cell in the document.
42
42
43 // First, attempt to be smart by guessing the index of the cell we are
43 // First, attempt to be smart by guessing the index of the cell we are
44 // scrolled to. Then, walk from there up or down until the right cell
44 // scrolled to. Then, walk from there up or down until the right cell
45 // is found. To guess the index, get the top of the last cell, and
45 // is found. To guess the index, get the top of the last cell, and
46 // divide that by the number of cells to get an average cell height.
46 // divide that by the number of cells to get an average cell height.
47 // Then divide the scroll height by the average cell height.
47 // Then divide the scroll height by the average cell height.
48 var cell_count = this.notebook.ncells();
48 var cell_count = this.notebook.ncells();
49 var first_cell_top = this.notebook.get_cell(0).element.offset().top;
49 var first_cell_top = this.notebook.get_cell(0).element.offset().top;
50 var last_cell_top = this.notebook.get_cell(cell_count-1).element.offset().top;
50 var last_cell_top = this.notebook.get_cell(cell_count-1).element.offset().top;
51 var avg_cell_height = (last_cell_top - first_cell_top) / cell_count;
51 var avg_cell_height = (last_cell_top - first_cell_top) / cell_count;
52 var notebook = $('#notebook');
52 var notebook = $('#notebook');
53 var i = Math.ceil(notebook.scrollTop() / avg_cell_height);
53 var i = Math.ceil(notebook.scrollTop() / avg_cell_height);
54 i = Math.min(Math.max(i , 0), cell_count - 1);
54 i = Math.min(Math.max(i , 0), cell_count - 1);
55
55
56 while (this.notebook.get_cell(i).element.offset().top - first_cell_top < notebook.scrollTop() && i < cell_count - 1) {
56 while (this.notebook.get_cell(i).element.offset().top - first_cell_top < notebook.scrollTop() && i < cell_count - 1) {
57 i += 1;
57 i += 1;
58 }
58 }
59
59
60 while (this.notebook.get_cell(i).element.offset().top - first_cell_top > notebook.scrollTop() - 50 && i >= 0) {
60 while (this.notebook.get_cell(i).element.offset().top - first_cell_top > notebook.scrollTop() - 50 && i >= 0) {
61 i -= 1;
61 i -= 1;
62 }
62 }
63 return Math.min(i + 1, cell_count - 1);
63 return Math.min(i + 1, cell_count - 1);
64 };
64 };
65
65
66
66
67 var TargetScrollManager = function(notebook, options) {
67 var TargetScrollManager = function(notebook, options) {
68 // Public constructor.
68 // Public constructor.
69 ScrollManager.apply(this, [notebook, options]);
69 ScrollManager.apply(this, [notebook, options]);
70 };
70 };
71 TargetScrollManager.prototype = new ScrollManager();
71 TargetScrollManager.prototype = Object.create(ScrollManager.prototype);
72
72
73 TargetScrollManager.prototype.is_target = function (index) {
73 TargetScrollManager.prototype.is_target = function (index) {
74 // Check if a cell should be a scroll stop.
74 // Check if a cell should be a scroll stop.
75 //
75 //
76 // Returns `true` if the cell is a cell that the scroll manager
76 // Returns `true` if the cell is a cell that the scroll manager
77 // should scroll to. Otherwise, false is returned.
77 // should scroll to. Otherwise, false is returned.
78 //
78 //
79 // Parameters
79 // Parameters
80 // ----------
80 // ----------
81 // index: integer
81 // index: integer
82 // index of the cell to test.
82 // index of the cell to test.
83 return false;
83 return false;
84 };
84 };
85
85
86 TargetScrollManager.prototype.scroll = function (delta) {
86 TargetScrollManager.prototype.scroll = function (delta) {
87 // Scroll the document.
87 // Scroll the document.
88 //
88 //
89 // Parameters
89 // Parameters
90 // ----------
90 // ----------
91 // delta: integer
91 // delta: integer
92 // direction to scroll the document. Positive is downwards.
92 // direction to scroll the document. Positive is downwards.
93 // Units are targets.
93 // Units are targets.
94
94
95 // Try to scroll to the next slide.
95 // Try to scroll to the next slide.
96 var cell_count = this.notebook.ncells();
96 var cell_count = this.notebook.ncells();
97 var selected_index = this.get_first_visible_cell() + delta;
97 var selected_index = this.get_first_visible_cell() + delta;
98 while (0 <= selected_index && selected_index < cell_count && !this.is_target(selected_index)) {
98 while (0 <= selected_index && selected_index < cell_count && !this.is_target(selected_index)) {
99 selected_index += delta;
99 selected_index += delta;
100 }
100 }
101
101
102 if (selected_index < 0 || cell_count <= selected_index) {
102 if (selected_index < 0 || cell_count <= selected_index) {
103 return ScrollManager.prototype.scroll.apply(this, [delta]);
103 return ScrollManager.prototype.scroll.apply(this, [delta]);
104 } else {
104 } else {
105 this.scroll_to(this.notebook.get_cell(selected_index).element);
105 this.scroll_to(this.notebook.get_cell(selected_index).element);
106
106
107 // Cancel browser keyboard scroll.
107 // Cancel browser keyboard scroll.
108 return false;
108 return false;
109 }
109 }
110 };
110 };
111
111
112
112
113 var SlideScrollManager = function(notebook, options) {
113 var SlideScrollManager = function(notebook, options) {
114 // Public constructor.
114 // Public constructor.
115 TargetScrollManager.apply(this, [notebook, options]);
115 TargetScrollManager.apply(this, [notebook, options]);
116 };
116 };
117 SlideScrollManager.prototype = new TargetScrollManager();
117 SlideScrollManager.prototype = Object.create(TargetScrollManager.prototype);
118
118
119 SlideScrollManager.prototype.is_target = function (index) {
119 SlideScrollManager.prototype.is_target = function (index) {
120 var cell = this.notebook.get_cell(index);
120 var cell = this.notebook.get_cell(index);
121 return cell.metadata && cell.metadata.slideshow &&
121 return cell.metadata && cell.metadata.slideshow &&
122 cell.metadata.slideshow.slide_type &&
122 cell.metadata.slideshow.slide_type &&
123 (cell.metadata.slideshow.slide_type === "slide" ||
123 (cell.metadata.slideshow.slide_type === "slide" ||
124 cell.metadata.slideshow.slide_type === "subslide");
124 cell.metadata.slideshow.slide_type === "subslide");
125 };
125 };
126
126
127
127
128 var HeadingScrollManager = function(notebook, options) {
128 var HeadingScrollManager = function(notebook, options) {
129 // Public constructor.
129 // Public constructor.
130 ScrollManager.apply(this, [notebook, options]);
130 ScrollManager.apply(this, [notebook, options]);
131 options = options || {};
131 options = options || {};
132 this._level = options.heading_level || 1;
132 this._level = options.heading_level || 1;
133 };
133 };
134 HeadingScrollManager.prototype = new ScrollManager();
134 HeadingScrollManager.prototype = Object.create(ScrollManager.prototype)
135
135
136 HeadingScrollManager.prototype.scroll = function (delta) {
136 HeadingScrollManager.prototype.scroll = function (delta) {
137 // Scroll the document.
137 // Scroll the document.
138 //
138 //
139 // Parameters
139 // Parameters
140 // ----------
140 // ----------
141 // delta: integer
141 // delta: integer
142 // direction to scroll the document. Positive is downwards.
142 // direction to scroll the document. Positive is downwards.
143 // Units are headers.
143 // Units are headers.
144
144
145 // Get all of the header elements that match the heading level or are of
145 // Get all of the header elements that match the heading level or are of
146 // greater magnitude (a smaller header number).
146 // greater magnitude (a smaller header number).
147 var headers = $();
147 var headers = $();
148 var i;
148 var i;
149 for (i = 1; i <= this._level; i++) {
149 for (i = 1; i <= this._level; i++) {
150 headers = headers.add('#notebook-container h' + i);
150 headers = headers.add('#notebook-container h' + i);
151 }
151 }
152
152
153 // Find the header the user is on or below.
153 // Find the header the user is on or below.
154 var first_cell_top = this.notebook.get_cell(0).element.offset().top;
154 var first_cell_top = this.notebook.get_cell(0).element.offset().top;
155 var notebook = $('#notebook');
155 var notebook = $('#notebook');
156 var current_scroll = notebook.scrollTop();
156 var current_scroll = notebook.scrollTop();
157 var header_scroll = 0;
157 var header_scroll = 0;
158 i = -1;
158 i = -1;
159 while (current_scroll >= header_scroll && i < headers.length) {
159 while (current_scroll >= header_scroll && i < headers.length) {
160 if (++i < headers.length) {
160 if (++i < headers.length) {
161 header_scroll = $(headers[i]).offset().top - first_cell_top;
161 header_scroll = $(headers[i]).offset().top - first_cell_top;
162 }
162 }
163 }
163 }
164 i--;
164 i--;
165
165
166 // Check if the user is below the header.
166 // Check if the user is below the header.
167 if (i < 0 || current_scroll > $(headers[i]).offset().top - first_cell_top + 30) {
167 if (i < 0 || current_scroll > $(headers[i]).offset().top - first_cell_top + 30) {
168 // Below the header, count the header as a target.
168 // Below the header, count the header as a target.
169 if (delta < 0) {
169 if (delta < 0) {
170 delta += 1;
170 delta += 1;
171 }
171 }
172 }
172 }
173 i += delta;
173 i += delta;
174
174
175 // Scroll!
175 // Scroll!
176 if (0 <= i && i < headers.length) {
176 if (0 <= i && i < headers.length) {
177 this.scroll_to(headers[i]);
177 this.scroll_to(headers[i]);
178 return false;
178 return false;
179 } else {
179 } else {
180 // Default to the base's scroll behavior when target header doesn't
180 // Default to the base's scroll behavior when target header doesn't
181 // exist.
181 // exist.
182 return ScrollManager.prototype.scroll.apply(this, [delta]);
182 return ScrollManager.prototype.scroll.apply(this, [delta]);
183 }
183 }
184 };
184 };
185
185
186 // Return naemspace for require.js loads
186 // Return naemspace for require.js loads
187 return {
187 return {
188 'ScrollManager': ScrollManager,
188 'ScrollManager': ScrollManager,
189 'SlideScrollManager': SlideScrollManager,
189 'SlideScrollManager': SlideScrollManager,
190 'HeadingScrollManager': HeadingScrollManager,
190 'HeadingScrollManager': HeadingScrollManager,
191 'TargetScrollManager': TargetScrollManager
191 'TargetScrollManager': TargetScrollManager
192 };
192 };
193 });
193 });
@@ -1,424 +1,424
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 'base/js/utils',
6 'base/js/utils',
7 'jquery',
7 'jquery',
8 'notebook/js/cell',
8 'notebook/js/cell',
9 'base/js/security',
9 'base/js/security',
10 'notebook/js/mathjaxutils',
10 'notebook/js/mathjaxutils',
11 'notebook/js/celltoolbar',
11 'notebook/js/celltoolbar',
12 'components/marked/lib/marked',
12 'components/marked/lib/marked',
13 'codemirror/lib/codemirror',
13 'codemirror/lib/codemirror',
14 'codemirror/mode/gfm/gfm',
14 'codemirror/mode/gfm/gfm',
15 'notebook/js/codemirror-ipythongfm'
15 'notebook/js/codemirror-ipythongfm'
16 ], function(IPython,utils , $, cell, security, mathjaxutils, celltoolbar, marked, CodeMirror, gfm, ipgfm) {
16 ], function(IPython,utils , $, cell, security, mathjaxutils, celltoolbar, marked, CodeMirror, gfm, ipgfm) {
17 "use strict";
17 "use strict";
18 var Cell = cell.Cell;
18 var Cell = cell.Cell;
19
19
20 var TextCell = function (options) {
20 var TextCell = function (options) {
21 // Constructor
21 // Constructor
22 //
22 //
23 // Construct a new TextCell, codemirror mode is by default 'htmlmixed',
23 // Construct a new TextCell, codemirror mode is by default 'htmlmixed',
24 // and cell type is 'text' cell start as not redered.
24 // and cell type is 'text' cell start as not redered.
25 //
25 //
26 // Parameters:
26 // Parameters:
27 // options: dictionary
27 // options: dictionary
28 // Dictionary of keyword arguments.
28 // Dictionary of keyword arguments.
29 // events: $(Events) instance
29 // events: $(Events) instance
30 // config: dictionary
30 // config: dictionary
31 // keyboard_manager: KeyboardManager instance
31 // keyboard_manager: KeyboardManager instance
32 // notebook: Notebook instance
32 // notebook: Notebook instance
33 options = options || {};
33 options = options || {};
34
34
35 // in all TextCell/Cell subclasses
35 // in all TextCell/Cell subclasses
36 // do not assign most of members here, just pass it down
36 // do not assign most of members here, just pass it down
37 // in the options dict potentially overwriting what you wish.
37 // in the options dict potentially overwriting what you wish.
38 // they will be assigned in the base class.
38 // they will be assigned in the base class.
39 this.notebook = options.notebook;
39 this.notebook = options.notebook;
40 this.events = options.events;
40 this.events = options.events;
41 this.config = options.config;
41 this.config = options.config;
42
42
43 // we cannot put this as a class key as it has handle to "this".
43 // we cannot put this as a class key as it has handle to "this".
44 var cm_overwrite_options = {
44 var cm_overwrite_options = {
45 onKeyEvent: $.proxy(this.handle_keyevent,this)
45 onKeyEvent: $.proxy(this.handle_keyevent,this)
46 };
46 };
47 var config = utils.mergeopt(TextCell, this.config, {cm_config:cm_overwrite_options});
47 var config = utils.mergeopt(TextCell, this.config, {cm_config:cm_overwrite_options});
48 Cell.apply(this, [{
48 Cell.apply(this, [{
49 config: config,
49 config: config,
50 keyboard_manager: options.keyboard_manager,
50 keyboard_manager: options.keyboard_manager,
51 events: this.events}]);
51 events: this.events}]);
52
52
53 this.cell_type = this.cell_type || 'text';
53 this.cell_type = this.cell_type || 'text';
54 mathjaxutils = mathjaxutils;
54 mathjaxutils = mathjaxutils;
55 this.rendered = false;
55 this.rendered = false;
56 };
56 };
57
57
58 TextCell.prototype = new Cell();
58 TextCell.prototype = Object.create(Cell.prototype);
59
59
60 TextCell.options_default = {
60 TextCell.options_default = {
61 cm_config : {
61 cm_config : {
62 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
62 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
63 mode: 'htmlmixed',
63 mode: 'htmlmixed',
64 lineWrapping : true,
64 lineWrapping : true,
65 }
65 }
66 };
66 };
67
67
68
68
69 /**
69 /**
70 * Create the DOM element of the TextCell
70 * Create the DOM element of the TextCell
71 * @method create_element
71 * @method create_element
72 * @private
72 * @private
73 */
73 */
74 TextCell.prototype.create_element = function () {
74 TextCell.prototype.create_element = function () {
75 Cell.prototype.create_element.apply(this, arguments);
75 Cell.prototype.create_element.apply(this, arguments);
76
76
77 var cell = $("<div>").addClass('cell text_cell');
77 var cell = $("<div>").addClass('cell text_cell');
78 cell.attr('tabindex','2');
78 cell.attr('tabindex','2');
79
79
80 var prompt = $('<div/>').addClass('prompt input_prompt');
80 var prompt = $('<div/>').addClass('prompt input_prompt');
81 cell.append(prompt);
81 cell.append(prompt);
82 var inner_cell = $('<div/>').addClass('inner_cell');
82 var inner_cell = $('<div/>').addClass('inner_cell');
83 this.celltoolbar = new celltoolbar.CellToolbar({
83 this.celltoolbar = new celltoolbar.CellToolbar({
84 cell: this,
84 cell: this,
85 notebook: this.notebook});
85 notebook: this.notebook});
86 inner_cell.append(this.celltoolbar.element);
86 inner_cell.append(this.celltoolbar.element);
87 var input_area = $('<div/>').addClass('input_area');
87 var input_area = $('<div/>').addClass('input_area');
88 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
88 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
89 // The tabindex=-1 makes this div focusable.
89 // The tabindex=-1 makes this div focusable.
90 var render_area = $('<div/>').addClass('text_cell_render rendered_html')
90 var render_area = $('<div/>').addClass('text_cell_render rendered_html')
91 .attr('tabindex','-1');
91 .attr('tabindex','-1');
92 inner_cell.append(input_area).append(render_area);
92 inner_cell.append(input_area).append(render_area);
93 cell.append(inner_cell);
93 cell.append(inner_cell);
94 this.element = cell;
94 this.element = cell;
95 };
95 };
96
96
97
97
98 // Cell level actions
98 // Cell level actions
99
99
100 TextCell.prototype.select = function () {
100 TextCell.prototype.select = function () {
101 var cont = Cell.prototype.select.apply(this);
101 var cont = Cell.prototype.select.apply(this);
102 if (cont) {
102 if (cont) {
103 if (this.mode === 'edit') {
103 if (this.mode === 'edit') {
104 this.code_mirror.refresh();
104 this.code_mirror.refresh();
105 }
105 }
106 }
106 }
107 return cont;
107 return cont;
108 };
108 };
109
109
110 TextCell.prototype.unrender = function () {
110 TextCell.prototype.unrender = function () {
111 if (this.read_only) return;
111 if (this.read_only) return;
112 var cont = Cell.prototype.unrender.apply(this);
112 var cont = Cell.prototype.unrender.apply(this);
113 if (cont) {
113 if (cont) {
114 var text_cell = this.element;
114 var text_cell = this.element;
115 var output = text_cell.find("div.text_cell_render");
115 var output = text_cell.find("div.text_cell_render");
116 if (this.get_text() === this.placeholder) {
116 if (this.get_text() === this.placeholder) {
117 this.set_text('');
117 this.set_text('');
118 }
118 }
119 this.refresh();
119 this.refresh();
120 }
120 }
121 return cont;
121 return cont;
122 };
122 };
123
123
124 TextCell.prototype.execute = function () {
124 TextCell.prototype.execute = function () {
125 this.render();
125 this.render();
126 };
126 };
127
127
128 /**
128 /**
129 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
129 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
130 * @method get_text
130 * @method get_text
131 * @retrun {string} CodeMirror current text value
131 * @retrun {string} CodeMirror current text value
132 */
132 */
133 TextCell.prototype.get_text = function() {
133 TextCell.prototype.get_text = function() {
134 return this.code_mirror.getValue();
134 return this.code_mirror.getValue();
135 };
135 };
136
136
137 /**
137 /**
138 * @param {string} text - Codemiror text value
138 * @param {string} text - Codemiror text value
139 * @see TextCell#get_text
139 * @see TextCell#get_text
140 * @method set_text
140 * @method set_text
141 * */
141 * */
142 TextCell.prototype.set_text = function(text) {
142 TextCell.prototype.set_text = function(text) {
143 this.code_mirror.setValue(text);
143 this.code_mirror.setValue(text);
144 this.unrender();
144 this.unrender();
145 this.code_mirror.refresh();
145 this.code_mirror.refresh();
146 };
146 };
147
147
148 /**
148 /**
149 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
149 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
150 * @method get_rendered
150 * @method get_rendered
151 * */
151 * */
152 TextCell.prototype.get_rendered = function() {
152 TextCell.prototype.get_rendered = function() {
153 return this.element.find('div.text_cell_render').html();
153 return this.element.find('div.text_cell_render').html();
154 };
154 };
155
155
156 /**
156 /**
157 * @method set_rendered
157 * @method set_rendered
158 */
158 */
159 TextCell.prototype.set_rendered = function(text) {
159 TextCell.prototype.set_rendered = function(text) {
160 this.element.find('div.text_cell_render').html(text);
160 this.element.find('div.text_cell_render').html(text);
161 };
161 };
162
162
163
163
164 /**
164 /**
165 * Create Text cell from JSON
165 * Create Text cell from JSON
166 * @param {json} data - JSON serialized text-cell
166 * @param {json} data - JSON serialized text-cell
167 * @method fromJSON
167 * @method fromJSON
168 */
168 */
169 TextCell.prototype.fromJSON = function (data) {
169 TextCell.prototype.fromJSON = function (data) {
170 Cell.prototype.fromJSON.apply(this, arguments);
170 Cell.prototype.fromJSON.apply(this, arguments);
171 if (data.cell_type === this.cell_type) {
171 if (data.cell_type === this.cell_type) {
172 if (data.source !== undefined) {
172 if (data.source !== undefined) {
173 this.set_text(data.source);
173 this.set_text(data.source);
174 // make this value the starting point, so that we can only undo
174 // make this value the starting point, so that we can only undo
175 // to this state, instead of a blank cell
175 // to this state, instead of a blank cell
176 this.code_mirror.clearHistory();
176 this.code_mirror.clearHistory();
177 // TODO: This HTML needs to be treated as potentially dangerous
177 // TODO: This HTML needs to be treated as potentially dangerous
178 // user input and should be handled before set_rendered.
178 // user input and should be handled before set_rendered.
179 this.set_rendered(data.rendered || '');
179 this.set_rendered(data.rendered || '');
180 this.rendered = false;
180 this.rendered = false;
181 this.render();
181 this.render();
182 }
182 }
183 }
183 }
184 };
184 };
185
185
186 /** Generate JSON from cell
186 /** Generate JSON from cell
187 * @return {object} cell data serialised to json
187 * @return {object} cell data serialised to json
188 */
188 */
189 TextCell.prototype.toJSON = function () {
189 TextCell.prototype.toJSON = function () {
190 var data = Cell.prototype.toJSON.apply(this);
190 var data = Cell.prototype.toJSON.apply(this);
191 data.source = this.get_text();
191 data.source = this.get_text();
192 if (data.source == this.placeholder) {
192 if (data.source == this.placeholder) {
193 data.source = "";
193 data.source = "";
194 }
194 }
195 return data;
195 return data;
196 };
196 };
197
197
198
198
199 var MarkdownCell = function (options) {
199 var MarkdownCell = function (options) {
200 // Constructor
200 // Constructor
201 //
201 //
202 // Parameters:
202 // Parameters:
203 // options: dictionary
203 // options: dictionary
204 // Dictionary of keyword arguments.
204 // Dictionary of keyword arguments.
205 // events: $(Events) instance
205 // events: $(Events) instance
206 // config: dictionary
206 // config: dictionary
207 // keyboard_manager: KeyboardManager instance
207 // keyboard_manager: KeyboardManager instance
208 // notebook: Notebook instance
208 // notebook: Notebook instance
209 options = options || {};
209 options = options || {};
210 var config = utils.mergeopt(MarkdownCell, options.config);
210 var config = utils.mergeopt(MarkdownCell, options.config);
211 TextCell.apply(this, [$.extend({}, options, {config: config})]);
211 TextCell.apply(this, [$.extend({}, options, {config: config})]);
212
212
213 this.cell_type = 'markdown';
213 this.cell_type = 'markdown';
214 };
214 };
215
215
216 MarkdownCell.options_default = {
216 MarkdownCell.options_default = {
217 cm_config: {
217 cm_config: {
218 mode: 'ipythongfm'
218 mode: 'ipythongfm'
219 },
219 },
220 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
220 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
221 };
221 };
222
222
223 MarkdownCell.prototype = new TextCell();
223 MarkdownCell.prototype = Object.create(TextCell.prototype);
224
224
225 /**
225 /**
226 * @method render
226 * @method render
227 */
227 */
228 MarkdownCell.prototype.render = function () {
228 MarkdownCell.prototype.render = function () {
229 var cont = TextCell.prototype.render.apply(this);
229 var cont = TextCell.prototype.render.apply(this);
230 if (cont) {
230 if (cont) {
231 var text = this.get_text();
231 var text = this.get_text();
232 var math = null;
232 var math = null;
233 if (text === "") { text = this.placeholder; }
233 if (text === "") { text = this.placeholder; }
234 var text_and_math = mathjaxutils.remove_math(text);
234 var text_and_math = mathjaxutils.remove_math(text);
235 text = text_and_math[0];
235 text = text_and_math[0];
236 math = text_and_math[1];
236 math = text_and_math[1];
237 var html = marked.parser(marked.lexer(text));
237 var html = marked.parser(marked.lexer(text));
238 html = mathjaxutils.replace_math(html, math);
238 html = mathjaxutils.replace_math(html, math);
239 html = security.sanitize_html(html);
239 html = security.sanitize_html(html);
240 html = $($.parseHTML(html));
240 html = $($.parseHTML(html));
241 // links in markdown cells should open in new tabs
241 // links in markdown cells should open in new tabs
242 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
242 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
243 this.set_rendered(html);
243 this.set_rendered(html);
244 this.typeset();
244 this.typeset();
245 }
245 }
246 return cont;
246 return cont;
247 };
247 };
248
248
249
249
250 var RawCell = function (options) {
250 var RawCell = function (options) {
251 // Constructor
251 // Constructor
252 //
252 //
253 // Parameters:
253 // Parameters:
254 // options: dictionary
254 // options: dictionary
255 // Dictionary of keyword arguments.
255 // Dictionary of keyword arguments.
256 // events: $(Events) instance
256 // events: $(Events) instance
257 // config: dictionary
257 // config: dictionary
258 // keyboard_manager: KeyboardManager instance
258 // keyboard_manager: KeyboardManager instance
259 // notebook: Notebook instance
259 // notebook: Notebook instance
260 options = options || {};
260 options = options || {};
261 var config = utils.mergeopt(RawCell, options.config);
261 var config = utils.mergeopt(RawCell, options.config);
262 TextCell.apply(this, [$.extend({}, options, {config: config})]);
262 TextCell.apply(this, [$.extend({}, options, {config: config})]);
263
263
264 this.cell_type = 'raw';
264 this.cell_type = 'raw';
265 };
265 };
266
266
267 RawCell.options_default = {
267 RawCell.options_default = {
268 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
268 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
269 "It will not be rendered in the notebook. " +
269 "It will not be rendered in the notebook. " +
270 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
270 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
271 };
271 };
272
272
273 RawCell.prototype = new TextCell();
273 RawCell.prototype = Object.create(TextCell.prototype);
274
274
275 /** @method bind_events **/
275 /** @method bind_events **/
276 RawCell.prototype.bind_events = function () {
276 RawCell.prototype.bind_events = function () {
277 TextCell.prototype.bind_events.apply(this);
277 TextCell.prototype.bind_events.apply(this);
278 var that = this;
278 var that = this;
279 this.element.focusout(function() {
279 this.element.focusout(function() {
280 that.auto_highlight();
280 that.auto_highlight();
281 that.render();
281 that.render();
282 });
282 });
283
283
284 this.code_mirror.on('focus', function() { that.unrender(); });
284 this.code_mirror.on('focus', function() { that.unrender(); });
285 };
285 };
286
286
287 /**
287 /**
288 * Trigger autodetection of highlight scheme for current cell
288 * Trigger autodetection of highlight scheme for current cell
289 * @method auto_highlight
289 * @method auto_highlight
290 */
290 */
291 RawCell.prototype.auto_highlight = function () {
291 RawCell.prototype.auto_highlight = function () {
292 this._auto_highlight(this.config.raw_cell_highlight);
292 this._auto_highlight(this.config.raw_cell_highlight);
293 };
293 };
294
294
295 /** @method render **/
295 /** @method render **/
296 RawCell.prototype.render = function () {
296 RawCell.prototype.render = function () {
297 var cont = TextCell.prototype.render.apply(this);
297 var cont = TextCell.prototype.render.apply(this);
298 if (cont){
298 if (cont){
299 var text = this.get_text();
299 var text = this.get_text();
300 if (text === "") { text = this.placeholder; }
300 if (text === "") { text = this.placeholder; }
301 this.set_text(text);
301 this.set_text(text);
302 this.element.removeClass('rendered');
302 this.element.removeClass('rendered');
303 }
303 }
304 return cont;
304 return cont;
305 };
305 };
306
306
307
307
308 var HeadingCell = function (options) {
308 var HeadingCell = function (options) {
309 // Constructor
309 // Constructor
310 //
310 //
311 // Parameters:
311 // Parameters:
312 // options: dictionary
312 // options: dictionary
313 // Dictionary of keyword arguments.
313 // Dictionary of keyword arguments.
314 // events: $(Events) instance
314 // events: $(Events) instance
315 // config: dictionary
315 // config: dictionary
316 // keyboard_manager: KeyboardManager instance
316 // keyboard_manager: KeyboardManager instance
317 // notebook: Notebook instance
317 // notebook: Notebook instance
318 options = options || {};
318 options = options || {};
319 var config = utils.mergeopt(HeadingCell, options.config);
319 var config = utils.mergeopt(HeadingCell, options.config);
320 TextCell.apply(this, [$.extend({}, options, {config: config})]);
320 TextCell.apply(this, [$.extend({}, options, {config: config})]);
321
321
322 this.level = 1;
322 this.level = 1;
323 this.cell_type = 'heading';
323 this.cell_type = 'heading';
324 };
324 };
325
325
326 HeadingCell.options_default = {
326 HeadingCell.options_default = {
327 cm_config: {
327 cm_config: {
328 theme: 'heading-1'
328 theme: 'heading-1'
329 },
329 },
330 placeholder: "Type Heading Here"
330 placeholder: "Type Heading Here"
331 };
331 };
332
332
333 HeadingCell.prototype = new TextCell();
333 HeadingCell.prototype = Object.create(TextCell.prototype);
334
334
335 /** @method fromJSON */
335 /** @method fromJSON */
336 HeadingCell.prototype.fromJSON = function (data) {
336 HeadingCell.prototype.fromJSON = function (data) {
337 if (data.level !== undefined){
337 if (data.level !== undefined){
338 this.level = data.level;
338 this.level = data.level;
339 }
339 }
340 TextCell.prototype.fromJSON.apply(this, arguments);
340 TextCell.prototype.fromJSON.apply(this, arguments);
341 this.code_mirror.setOption("theme", "heading-"+this.level);
341 this.code_mirror.setOption("theme", "heading-"+this.level);
342 };
342 };
343
343
344
344
345 /** @method toJSON */
345 /** @method toJSON */
346 HeadingCell.prototype.toJSON = function () {
346 HeadingCell.prototype.toJSON = function () {
347 var data = TextCell.prototype.toJSON.apply(this);
347 var data = TextCell.prototype.toJSON.apply(this);
348 data.level = this.get_level();
348 data.level = this.get_level();
349 return data;
349 return data;
350 };
350 };
351
351
352 /**
352 /**
353 * Change heading level of cell, and re-render
353 * Change heading level of cell, and re-render
354 * @method set_level
354 * @method set_level
355 */
355 */
356 HeadingCell.prototype.set_level = function (level) {
356 HeadingCell.prototype.set_level = function (level) {
357 this.level = level;
357 this.level = level;
358 this.code_mirror.setOption("theme", "heading-"+level);
358 this.code_mirror.setOption("theme", "heading-"+level);
359
359
360 if (this.rendered) {
360 if (this.rendered) {
361 this.rendered = false;
361 this.rendered = false;
362 this.render();
362 this.render();
363 }
363 }
364 };
364 };
365
365
366 /** The depth of header cell, based on html (h1 to h6)
366 /** The depth of header cell, based on html (h1 to h6)
367 * @method get_level
367 * @method get_level
368 * @return {integer} level - for 1 to 6
368 * @return {integer} level - for 1 to 6
369 */
369 */
370 HeadingCell.prototype.get_level = function () {
370 HeadingCell.prototype.get_level = function () {
371 return this.level;
371 return this.level;
372 };
372 };
373
373
374
374
375 HeadingCell.prototype.get_rendered = function () {
375 HeadingCell.prototype.get_rendered = function () {
376 var r = this.element.find("div.text_cell_render");
376 var r = this.element.find("div.text_cell_render");
377 return r.children().first().html();
377 return r.children().first().html();
378 };
378 };
379
379
380 HeadingCell.prototype.render = function () {
380 HeadingCell.prototype.render = function () {
381 var cont = TextCell.prototype.render.apply(this);
381 var cont = TextCell.prototype.render.apply(this);
382 if (cont) {
382 if (cont) {
383 var text = this.get_text();
383 var text = this.get_text();
384 var math = null;
384 var math = null;
385 // Markdown headings must be a single line
385 // Markdown headings must be a single line
386 text = text.replace(/\n/g, ' ');
386 text = text.replace(/\n/g, ' ');
387 if (text === "") { text = this.placeholder; }
387 if (text === "") { text = this.placeholder; }
388 text = new Array(this.level + 1).join("#") + " " + text;
388 text = new Array(this.level + 1).join("#") + " " + text;
389 var text_and_math = mathjaxutils.remove_math(text);
389 var text_and_math = mathjaxutils.remove_math(text);
390 text = text_and_math[0];
390 text = text_and_math[0];
391 math = text_and_math[1];
391 math = text_and_math[1];
392 var html = marked.parser(marked.lexer(text));
392 var html = marked.parser(marked.lexer(text));
393 html = mathjaxutils.replace_math(html, math);
393 html = mathjaxutils.replace_math(html, math);
394 html = security.sanitize_html(html);
394 html = security.sanitize_html(html);
395 var h = $($.parseHTML(html));
395 var h = $($.parseHTML(html));
396 // add id and linkback anchor
396 // add id and linkback anchor
397 var hash = h.text().trim().replace(/ /g, '-');
397 var hash = h.text().trim().replace(/ /g, '-');
398 h.attr('id', hash);
398 h.attr('id', hash);
399 h.append(
399 h.append(
400 $('<a/>')
400 $('<a/>')
401 .addClass('anchor-link')
401 .addClass('anchor-link')
402 .attr('href', '#' + hash)
402 .attr('href', '#' + hash)
403 .text('¶')
403 .text('¶')
404 );
404 );
405 this.set_rendered(h);
405 this.set_rendered(h);
406 this.typeset();
406 this.typeset();
407 }
407 }
408 return cont;
408 return cont;
409 };
409 };
410
410
411 // Backwards compatability.
411 // Backwards compatability.
412 IPython.TextCell = TextCell;
412 IPython.TextCell = TextCell;
413 IPython.MarkdownCell = MarkdownCell;
413 IPython.MarkdownCell = MarkdownCell;
414 IPython.RawCell = RawCell;
414 IPython.RawCell = RawCell;
415 IPython.HeadingCell = HeadingCell;
415 IPython.HeadingCell = HeadingCell;
416
416
417 var textcell = {
417 var textcell = {
418 'TextCell': TextCell,
418 'TextCell': TextCell,
419 'MarkdownCell': MarkdownCell,
419 'MarkdownCell': MarkdownCell,
420 'RawCell': RawCell,
420 'RawCell': RawCell,
421 'HeadingCell': HeadingCell,
421 'HeadingCell': HeadingCell,
422 };
422 };
423 return textcell;
423 return textcell;
424 });
424 });
General Comments 0
You need to be logged in to leave comments. Login now