##// END OF EJS Templates
Javascript output is not run on notebook loading and paste's.
Brian Granger -
Show More
@@ -1,880 +1,884
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var CodeCell = function (notebook) {
16 var CodeCell = function (notebook) {
17 this.code_mirror = null;
17 this.code_mirror = null;
18 this.input_prompt_number = null;
18 this.input_prompt_number = null;
19 this.is_completing = false;
19 this.is_completing = false;
20 this.completion_cursor = null;
20 this.completion_cursor = null;
21 this.outputs = [];
21 this.outputs = [];
22 this.collapsed = false;
22 this.collapsed = false;
23 this.tooltip_timeout = null;
23 this.tooltip_timeout = null;
24 IPython.Cell.apply(this, arguments);
24 IPython.Cell.apply(this, arguments);
25 };
25 };
26
26
27
27
28 CodeCell.prototype = new IPython.Cell();
28 CodeCell.prototype = new IPython.Cell();
29
29
30
30
31 CodeCell.prototype.create_element = function () {
31 CodeCell.prototype.create_element = function () {
32 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
32 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell vbox');
33 cell.attr('tabindex','2');
33 cell.attr('tabindex','2');
34 var input = $('<div></div>').addClass('input hbox');
34 var input = $('<div></div>').addClass('input hbox');
35 input.append($('<div/>').addClass('prompt input_prompt'));
35 input.append($('<div/>').addClass('prompt input_prompt'));
36 var input_area = $('<div/>').addClass('input_area box-flex1');
36 var input_area = $('<div/>').addClass('input_area box-flex1');
37 this.code_mirror = CodeMirror(input_area.get(0), {
37 this.code_mirror = CodeMirror(input_area.get(0), {
38 indentUnit : 4,
38 indentUnit : 4,
39 mode: 'python',
39 mode: 'python',
40 theme: 'ipython',
40 theme: 'ipython',
41 readOnly: this.read_only,
41 readOnly: this.read_only,
42 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
42 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
43 });
43 });
44 input.append(input_area);
44 input.append(input_area);
45 var output = $('<div></div>').addClass('output vbox');
45 var output = $('<div></div>').addClass('output vbox');
46 cell.append(input).append(output);
46 cell.append(input).append(output);
47 this.element = cell;
47 this.element = cell;
48 this.collapse();
48 this.collapse();
49 };
49 };
50
50
51 //TODO, try to diminish the number of parameters.
51 //TODO, try to diminish the number of parameters.
52 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
52 CodeCell.prototype.request_tooltip_after_time = function (pre_cursor,time){
53 var that = this;
53 var that = this;
54 if (pre_cursor === "" || pre_cursor === "(" ) {
54 if (pre_cursor === "" || pre_cursor === "(" ) {
55 // don't do anything if line beggin with '(' or is empty
55 // don't do anything if line beggin with '(' or is empty
56 } else {
56 } else {
57 // Will set a timer to request tooltip in `time`
57 // Will set a timer to request tooltip in `time`
58 that.tooltip_timeout = setTimeout(function(){
58 that.tooltip_timeout = setTimeout(function(){
59 IPython.notebook.request_tool_tip(that, pre_cursor)
59 IPython.notebook.request_tool_tip(that, pre_cursor)
60 },time);
60 },time);
61 }
61 }
62 };
62 };
63
63
64 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
64 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
65 // This method gets called in CodeMirror's onKeyDown/onKeyPress
65 // This method gets called in CodeMirror's onKeyDown/onKeyPress
66 // handlers and is used to provide custom key handling. Its return
66 // handlers and is used to provide custom key handling. Its return
67 // value is used to determine if CodeMirror should ignore the event:
67 // value is used to determine if CodeMirror should ignore the event:
68 // true = ignore, false = don't ignore.
68 // true = ignore, false = don't ignore.
69
69
70 if (this.read_only){
70 if (this.read_only){
71 return false;
71 return false;
72 }
72 }
73
73
74 // note that we are comparing and setting the time to wait at each key press.
74 // note that we are comparing and setting the time to wait at each key press.
75 // a better wqy might be to generate a new function on each time change and
75 // a better wqy might be to generate a new function on each time change and
76 // assign it to CodeCell.prototype.request_tooltip_after_time
76 // assign it to CodeCell.prototype.request_tooltip_after_time
77 tooltip_wait_time = this.notebook.time_before_tooltip;
77 tooltip_wait_time = this.notebook.time_before_tooltip;
78 tooltip_on_tab = this.notebook.tooltip_on_tab;
78 tooltip_on_tab = this.notebook.tooltip_on_tab;
79 var that = this;
79 var that = this;
80 // whatever key is pressed, first, cancel the tooltip request before
80 // whatever key is pressed, first, cancel the tooltip request before
81 // they are sent, and remove tooltip if any
81 // they are sent, and remove tooltip if any
82 if(event.type === 'keydown' ) {
82 if(event.type === 'keydown' ) {
83 that.remove_and_cancel_tooltip();
83 that.remove_and_cancel_tooltip();
84 };
84 };
85
85
86
86
87 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
87 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
88 // Always ignore shift-enter in CodeMirror as we handle it.
88 // Always ignore shift-enter in CodeMirror as we handle it.
89 return true;
89 return true;
90 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
90 } else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
91 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
91 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
92 // browser and keyboard layout !
92 // browser and keyboard layout !
93 // Pressing '(' , request tooltip, don't forget to reappend it
93 // Pressing '(' , request tooltip, don't forget to reappend it
94 var cursor = editor.getCursor();
94 var cursor = editor.getCursor();
95 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
95 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
96 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
96 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
97 } else if (event.which === 38) {
97 } else if (event.which === 38) {
98 // If we are not at the top, let CM handle the up arrow and
98 // If we are not at the top, let CM handle the up arrow and
99 // prevent the global keydown handler from handling it.
99 // prevent the global keydown handler from handling it.
100 if (!that.at_top()) {
100 if (!that.at_top()) {
101 event.stop();
101 event.stop();
102 return false;
102 return false;
103 } else {
103 } else {
104 return true;
104 return true;
105 };
105 };
106 } else if (event.which === 40) {
106 } else if (event.which === 40) {
107 // If we are not at the bottom, let CM handle the down arrow and
107 // If we are not at the bottom, let CM handle the down arrow and
108 // prevent the global keydown handler from handling it.
108 // prevent the global keydown handler from handling it.
109 if (!that.at_bottom()) {
109 if (!that.at_bottom()) {
110 event.stop();
110 event.stop();
111 return false;
111 return false;
112 } else {
112 } else {
113 return true;
113 return true;
114 };
114 };
115 } else if (event.keyCode === 9 && event.type == 'keydown') {
115 } else if (event.keyCode === 9 && event.type == 'keydown') {
116 // Tab completion.
116 // Tab completion.
117 var cur = editor.getCursor();
117 var cur = editor.getCursor();
118 //Do not trim here because of tooltip
118 //Do not trim here because of tooltip
119 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
119 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
120 if (pre_cursor.trim() === "") {
120 if (pre_cursor.trim() === "") {
121 // Don't autocomplete if the part of the line before the cursor
121 // Don't autocomplete if the part of the line before the cursor
122 // is empty. In this case, let CodeMirror handle indentation.
122 // is empty. In this case, let CodeMirror handle indentation.
123 return false;
123 return false;
124 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
124 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
125 that.request_tooltip_after_time(pre_cursor,0);
125 that.request_tooltip_after_time(pre_cursor,0);
126 // Prevent the event from bubbling up.
126 // Prevent the event from bubbling up.
127 event.stop();
127 event.stop();
128 // Prevent CodeMirror from handling the tab.
128 // Prevent CodeMirror from handling the tab.
129 return true;
129 return true;
130 } else {
130 } else {
131 pre_cursor.trim();
131 pre_cursor.trim();
132 // Autocomplete the current line.
132 // Autocomplete the current line.
133 event.stop();
133 event.stop();
134 var line = editor.getLine(cur.line);
134 var line = editor.getLine(cur.line);
135 this.is_completing = true;
135 this.is_completing = true;
136 this.completion_cursor = cur;
136 this.completion_cursor = cur;
137 IPython.notebook.complete_cell(this, line, cur.ch);
137 IPython.notebook.complete_cell(this, line, cur.ch);
138 return true;
138 return true;
139 };
139 };
140 } else if (event.keyCode === 8 && event.type == 'keydown') {
140 } else if (event.keyCode === 8 && event.type == 'keydown') {
141 // If backspace and the line ends with 4 spaces, remove them.
141 // If backspace and the line ends with 4 spaces, remove them.
142 var cur = editor.getCursor();
142 var cur = editor.getCursor();
143 var line = editor.getLine(cur.line);
143 var line = editor.getLine(cur.line);
144 var ending = line.slice(-4);
144 var ending = line.slice(-4);
145 if (ending === ' ') {
145 if (ending === ' ') {
146 editor.replaceRange('',
146 editor.replaceRange('',
147 {line: cur.line, ch: cur.ch-4},
147 {line: cur.line, ch: cur.ch-4},
148 {line: cur.line, ch: cur.ch}
148 {line: cur.line, ch: cur.ch}
149 );
149 );
150 event.stop();
150 event.stop();
151 return true;
151 return true;
152 } else {
152 } else {
153 return false;
153 return false;
154 };
154 };
155 } else {
155 } else {
156 // keypress/keyup also trigger on TAB press, and we don't want to
156 // keypress/keyup also trigger on TAB press, and we don't want to
157 // use those to disable tab completion.
157 // use those to disable tab completion.
158 if (this.is_completing && event.keyCode !== 9) {
158 if (this.is_completing && event.keyCode !== 9) {
159 var ed_cur = editor.getCursor();
159 var ed_cur = editor.getCursor();
160 var cc_cur = this.completion_cursor;
160 var cc_cur = this.completion_cursor;
161 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
161 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
162 this.is_completing = false;
162 this.is_completing = false;
163 this.completion_cursor = null;
163 this.completion_cursor = null;
164 };
164 };
165 };
165 };
166 return false;
166 return false;
167 };
167 };
168 return false;
168 return false;
169 };
169 };
170
170
171 CodeCell.prototype.remove_and_cancel_tooltip = function() {
171 CodeCell.prototype.remove_and_cancel_tooltip = function() {
172 // note that we don't handle closing directly inside the calltip
172 // note that we don't handle closing directly inside the calltip
173 // as in the completer, because it is not focusable, so won't
173 // as in the completer, because it is not focusable, so won't
174 // get the event.
174 // get the event.
175 if (this.tooltip_timeout != null){
175 if (this.tooltip_timeout != null){
176 clearTimeout(this.tooltip_timeout);
176 clearTimeout(this.tooltip_timeout);
177 $('#tooltip').remove();
177 $('#tooltip').remove();
178 this.tooltip_timeout = null;
178 this.tooltip_timeout = null;
179 }
179 }
180 }
180 }
181
181
182 CodeCell.prototype.finish_tooltip = function (reply) {
182 CodeCell.prototype.finish_tooltip = function (reply) {
183 // Extract call tip data; the priority is call, init, main.
183 // Extract call tip data; the priority is call, init, main.
184 defstring = reply.call_def;
184 defstring = reply.call_def;
185 if (defstring == null) { defstring = reply.init_definition; }
185 if (defstring == null) { defstring = reply.init_definition; }
186 if (defstring == null) { defstring = reply.definition; }
186 if (defstring == null) { defstring = reply.definition; }
187
187
188 docstring = reply.call_docstring;
188 docstring = reply.call_docstring;
189 if (docstring == null) { docstring = reply.init_docstring; }
189 if (docstring == null) { docstring = reply.init_docstring; }
190 if (docstring == null) { docstring = reply.docstring; }
190 if (docstring == null) { docstring = reply.docstring; }
191 if (docstring == null) { docstring = "<empty docstring>"; }
191 if (docstring == null) { docstring = "<empty docstring>"; }
192
192
193 name=reply.name;
193 name=reply.name;
194
194
195 var that = this;
195 var that = this;
196 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
196 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
197 // remove to have the tooltip not Limited in X and Y
197 // remove to have the tooltip not Limited in X and Y
198 tooltip.addClass('smalltooltip');
198 tooltip.addClass('smalltooltip');
199 var pre=$('<pre/>').html(utils.fixConsole(docstring));
199 var pre=$('<pre/>').html(utils.fixConsole(docstring));
200 var expandlink=$('<a/>').attr('href',"#");
200 var expandlink=$('<a/>').attr('href',"#");
201 expandlink.addClass("ui-corner-all"); //rounded corner
201 expandlink.addClass("ui-corner-all"); //rounded corner
202 expandlink.attr('role',"button");
202 expandlink.attr('role',"button");
203 //expandlink.addClass('ui-button');
203 //expandlink.addClass('ui-button');
204 //expandlink.addClass('ui-state-default');
204 //expandlink.addClass('ui-state-default');
205 var expandspan=$('<span/>').text('Expand');
205 var expandspan=$('<span/>').text('Expand');
206 expandspan.addClass('ui-icon');
206 expandspan.addClass('ui-icon');
207 expandspan.addClass('ui-icon-plus');
207 expandspan.addClass('ui-icon-plus');
208 expandlink.append(expandspan);
208 expandlink.append(expandspan);
209 expandlink.attr('id','expanbutton');
209 expandlink.attr('id','expanbutton');
210 expandlink.click(function(){
210 expandlink.click(function(){
211 tooltip.removeClass('smalltooltip');
211 tooltip.removeClass('smalltooltip');
212 tooltip.addClass('bigtooltip');
212 tooltip.addClass('bigtooltip');
213 $('#expanbutton').remove();
213 $('#expanbutton').remove();
214 setTimeout(function(){that.code_mirror.focus();}, 50);
214 setTimeout(function(){that.code_mirror.focus();}, 50);
215 });
215 });
216 var morelink=$('<a/>').attr('href',"#");
216 var morelink=$('<a/>').attr('href',"#");
217 morelink.attr('role',"button");
217 morelink.attr('role',"button");
218 morelink.addClass('ui-button');
218 morelink.addClass('ui-button');
219 //morelink.addClass("ui-corner-all"); //rounded corner
219 //morelink.addClass("ui-corner-all"); //rounded corner
220 //morelink.addClass('ui-state-default');
220 //morelink.addClass('ui-state-default');
221 var morespan=$('<span/>').text('Open in Pager');
221 var morespan=$('<span/>').text('Open in Pager');
222 morespan.addClass('ui-icon');
222 morespan.addClass('ui-icon');
223 morespan.addClass('ui-icon-arrowstop-l-n');
223 morespan.addClass('ui-icon-arrowstop-l-n');
224 morelink.append(morespan);
224 morelink.append(morespan);
225 morelink.click(function(){
225 morelink.click(function(){
226 var msg_id = IPython.notebook.kernel.execute(name+"?");
226 var msg_id = IPython.notebook.kernel.execute(name+"?");
227 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
227 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.get_selected_cell().cell_id;
228 that.remove_and_cancel_tooltip();
228 that.remove_and_cancel_tooltip();
229 setTimeout(function(){that.code_mirror.focus();}, 50);
229 setTimeout(function(){that.code_mirror.focus();}, 50);
230 });
230 });
231
231
232 var closelink=$('<a/>').attr('href',"#");
232 var closelink=$('<a/>').attr('href',"#");
233 closelink.attr('role',"button");
233 closelink.attr('role',"button");
234 closelink.addClass('ui-button');
234 closelink.addClass('ui-button');
235 //closelink.addClass("ui-corner-all"); //rounded corner
235 //closelink.addClass("ui-corner-all"); //rounded corner
236 //closelink.adClass('ui-state-default'); // grey background and blue cross
236 //closelink.adClass('ui-state-default'); // grey background and blue cross
237 var closespan=$('<span/>').text('Close');
237 var closespan=$('<span/>').text('Close');
238 closespan.addClass('ui-icon');
238 closespan.addClass('ui-icon');
239 closespan.addClass('ui-icon-close');
239 closespan.addClass('ui-icon-close');
240 closelink.append(closespan);
240 closelink.append(closespan);
241 closelink.click(function(){
241 closelink.click(function(){
242 that.remove_and_cancel_tooltip();
242 that.remove_and_cancel_tooltip();
243 setTimeout(function(){that.code_mirror.focus();}, 50);
243 setTimeout(function(){that.code_mirror.focus();}, 50);
244 });
244 });
245 //construct the tooltip
245 //construct the tooltip
246 tooltip.append(closelink);
246 tooltip.append(closelink);
247 tooltip.append(expandlink);
247 tooltip.append(expandlink);
248 tooltip.append(morelink);
248 tooltip.append(morelink);
249 if(defstring){
249 if(defstring){
250 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
250 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
251 tooltip.append(defstring_html);
251 tooltip.append(defstring_html);
252 }
252 }
253 tooltip.append(pre);
253 tooltip.append(pre);
254 var pos = this.code_mirror.cursorCoords();
254 var pos = this.code_mirror.cursorCoords();
255 tooltip.css('left',pos.x+'px');
255 tooltip.css('left',pos.x+'px');
256 tooltip.css('top',pos.yBot+'px');
256 tooltip.css('top',pos.yBot+'px');
257 $('body').append(tooltip);
257 $('body').append(tooltip);
258
258
259 // issues with cross-closing if multiple tooltip in less than 5sec
259 // issues with cross-closing if multiple tooltip in less than 5sec
260 // keep it comented for now
260 // keep it comented for now
261 // setTimeout(that.remove_and_cancel_tooltip, 5000);
261 // setTimeout(that.remove_and_cancel_tooltip, 5000);
262 };
262 };
263
263
264 // As you type completer
264 // As you type completer
265 CodeCell.prototype.finish_completing = function (matched_text, matches) {
265 CodeCell.prototype.finish_completing = function (matched_text, matches) {
266 if(matched_text[0]=='%'){
266 if(matched_text[0]=='%'){
267 completing_from_magic = true;
267 completing_from_magic = true;
268 completing_to_magic = false;
268 completing_to_magic = false;
269 } else {
269 } else {
270 completing_from_magic = false;
270 completing_from_magic = false;
271 completing_to_magic = false;
271 completing_to_magic = false;
272 }
272 }
273 //return if not completing or nothing to complete
273 //return if not completing or nothing to complete
274 if (!this.is_completing || matches.length === 0) {return;}
274 if (!this.is_completing || matches.length === 0) {return;}
275
275
276 // for later readability
276 // for later readability
277 var key = { tab:9,
277 var key = { tab:9,
278 esc:27,
278 esc:27,
279 backspace:8,
279 backspace:8,
280 space:32,
280 space:32,
281 shift:16,
281 shift:16,
282 enter:13,
282 enter:13,
283 // _ is 95
283 // _ is 95
284 isCompSymbol : function (code)
284 isCompSymbol : function (code)
285 {
285 {
286 return (code > 64 && code <= 90)
286 return (code > 64 && code <= 90)
287 || (code >= 97 && code <= 122)
287 || (code >= 97 && code <= 122)
288 || (code == 95)
288 || (code == 95)
289 },
289 },
290 dismissAndAppend : function (code)
290 dismissAndAppend : function (code)
291 {
291 {
292 chararr = '()[]+-/\\. ,=*'.split("");
292 chararr = '()[]+-/\\. ,=*'.split("");
293 codearr = chararr.map(function(x){return x.charCodeAt(0)});
293 codearr = chararr.map(function(x){return x.charCodeAt(0)});
294 return jQuery.inArray(code, codearr) != -1;
294 return jQuery.inArray(code, codearr) != -1;
295 }
295 }
296
296
297 }
297 }
298
298
299 // smart completion, sort kwarg ending with '='
299 // smart completion, sort kwarg ending with '='
300 var newm = new Array();
300 var newm = new Array();
301 if(this.notebook.smart_completer)
301 if(this.notebook.smart_completer)
302 {
302 {
303 kwargs = new Array();
303 kwargs = new Array();
304 other = new Array();
304 other = new Array();
305 for(var i = 0 ; i<matches.length ; ++i){
305 for(var i = 0 ; i<matches.length ; ++i){
306 if(matches[i].substr(-1) === '='){
306 if(matches[i].substr(-1) === '='){
307 kwargs.push(matches[i]);
307 kwargs.push(matches[i]);
308 }else{other.push(matches[i]);}
308 }else{other.push(matches[i]);}
309 }
309 }
310 newm = kwargs.concat(other);
310 newm = kwargs.concat(other);
311 matches = newm;
311 matches = newm;
312 }
312 }
313 // end sort kwargs
313 // end sort kwargs
314
314
315 // give common prefix of a array of string
315 // give common prefix of a array of string
316 function sharedStart(A){
316 function sharedStart(A){
317 shared='';
317 shared='';
318 if(A.length == 1){shared=A[0]}
318 if(A.length == 1){shared=A[0]}
319 if(A.length > 1 ){
319 if(A.length > 1 ){
320 var tem1, tem2, s, A = A.slice(0).sort();
320 var tem1, tem2, s, A = A.slice(0).sort();
321 tem1 = A[0];
321 tem1 = A[0];
322 s = tem1.length;
322 s = tem1.length;
323 tem2 = A.pop();
323 tem2 = A.pop();
324 while(s && tem2.indexOf(tem1) == -1){
324 while(s && tem2.indexOf(tem1) == -1){
325 tem1 = tem1.substring(0, --s);
325 tem1 = tem1.substring(0, --s);
326 }
326 }
327 shared = tem1;
327 shared = tem1;
328 }
328 }
329 if (shared[0] == '%' && !completing_from_magic)
329 if (shared[0] == '%' && !completing_from_magic)
330 {
330 {
331 shared = shared.substr(1);
331 shared = shared.substr(1);
332 return [shared, true];
332 return [shared, true];
333 } else {
333 } else {
334 return [shared, false];
334 return [shared, false];
335 }
335 }
336 }
336 }
337
337
338
338
339 //try to check if the user is typing tab at least twice after a word
339 //try to check if the user is typing tab at least twice after a word
340 // and completion is "done"
340 // and completion is "done"
341 fallback_on_tooltip_after = 2
341 fallback_on_tooltip_after = 2
342 if(matches.length == 1 && matched_text === matches[0])
342 if(matches.length == 1 && matched_text === matches[0])
343 {
343 {
344 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
344 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
345 {
345 {
346 this.request_tooltip_after_time(matched_text+'(',0);
346 this.request_tooltip_after_time(matched_text+'(',0);
347 return;
347 return;
348 }
348 }
349 this.prevmatch = matched_text
349 this.prevmatch = matched_text
350 this.npressed = this.npressed+1;
350 this.npressed = this.npressed+1;
351 }
351 }
352 else
352 else
353 {
353 {
354 this.prevmatch = "";
354 this.prevmatch = "";
355 this.npressed = 0;
355 this.npressed = 0;
356 }
356 }
357 // end fallback on tooltip
357 // end fallback on tooltip
358 //==================================
358 //==================================
359 // Real completion logic start here
359 // Real completion logic start here
360 var that = this;
360 var that = this;
361 var cur = this.completion_cursor;
361 var cur = this.completion_cursor;
362 var done = false;
362 var done = false;
363
363
364 // call to dismmiss the completer
364 // call to dismmiss the completer
365 var close = function () {
365 var close = function () {
366 if (done) return;
366 if (done) return;
367 done = true;
367 done = true;
368 if (complete != undefined)
368 if (complete != undefined)
369 {complete.remove();}
369 {complete.remove();}
370 that.is_completing = false;
370 that.is_completing = false;
371 that.completion_cursor = null;
371 that.completion_cursor = null;
372 };
372 };
373
373
374 // update codemirror with the typed text
374 // update codemirror with the typed text
375 prev = matched_text
375 prev = matched_text
376 var update = function (inserted_text, event) {
376 var update = function (inserted_text, event) {
377 that.code_mirror.replaceRange(
377 that.code_mirror.replaceRange(
378 inserted_text,
378 inserted_text,
379 {line: cur.line, ch: (cur.ch-matched_text.length)},
379 {line: cur.line, ch: (cur.ch-matched_text.length)},
380 {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
380 {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
381 );
381 );
382 prev = inserted_text
382 prev = inserted_text
383 if(event != null){
383 if(event != null){
384 event.stopPropagation();
384 event.stopPropagation();
385 event.preventDefault();
385 event.preventDefault();
386 }
386 }
387 };
387 };
388 // insert the given text and exit the completer
388 // insert the given text and exit the completer
389 var insert = function (selected_text, event) {
389 var insert = function (selected_text, event) {
390 update(selected_text)
390 update(selected_text)
391 close();
391 close();
392 setTimeout(function(){that.code_mirror.focus();}, 50);
392 setTimeout(function(){that.code_mirror.focus();}, 50);
393 };
393 };
394
394
395 // insert the curent highlited selection and exit
395 // insert the curent highlited selection and exit
396 var pick = function () {
396 var pick = function () {
397 insert(select.val()[0],null);
397 insert(select.val()[0],null);
398 };
398 };
399
399
400
400
401 // Define function to clear the completer, refill it with the new
401 // Define function to clear the completer, refill it with the new
402 // matches, update the pseuso typing field. autopick insert match if
402 // matches, update the pseuso typing field. autopick insert match if
403 // only one left, in no matches (anymore) dismiss itself by pasting
403 // only one left, in no matches (anymore) dismiss itself by pasting
404 // what the user have typed until then
404 // what the user have typed until then
405 var complete_with = function(matches,typed_text,autopick,event)
405 var complete_with = function(matches,typed_text,autopick,event)
406 {
406 {
407 // If autopick an only one match, past.
407 // If autopick an only one match, past.
408 // Used to 'pick' when pressing tab
408 // Used to 'pick' when pressing tab
409 var prefix = '';
409 var prefix = '';
410 if(completing_to_magic && !completing_from_magic)
410 if(completing_to_magic && !completing_from_magic)
411 {
411 {
412 prefix='%';
412 prefix='%';
413 }
413 }
414 if (matches.length < 1) {
414 if (matches.length < 1) {
415 insert(prefix+typed_text,event);
415 insert(prefix+typed_text,event);
416 if(event != null){
416 if(event != null){
417 event.stopPropagation();
417 event.stopPropagation();
418 event.preventDefault();
418 event.preventDefault();
419 }
419 }
420 } else if (autopick && matches.length == 1) {
420 } else if (autopick && matches.length == 1) {
421 insert(matches[0],event);
421 insert(matches[0],event);
422 if(event != null){
422 if(event != null){
423 event.stopPropagation();
423 event.stopPropagation();
424 event.preventDefault();
424 event.preventDefault();
425 }
425 }
426 return;
426 return;
427 }
427 }
428 //clear the previous completion if any
428 //clear the previous completion if any
429 update(prefix+typed_text,event);
429 update(prefix+typed_text,event);
430 complete.children().children().remove();
430 complete.children().children().remove();
431 $('#asyoutype').html("<b>"+prefix+matched_text+"</b>"+typed_text.substr(matched_text.length));
431 $('#asyoutype').html("<b>"+prefix+matched_text+"</b>"+typed_text.substr(matched_text.length));
432 select = $('#asyoutypeselect');
432 select = $('#asyoutypeselect');
433 for (var i = 0; i<matches.length; ++i) {
433 for (var i = 0; i<matches.length; ++i) {
434 select.append($('<option/>').html(matches[i]));
434 select.append($('<option/>').html(matches[i]));
435 }
435 }
436 select.children().first().attr('selected','true');
436 select.children().first().attr('selected','true');
437 }
437 }
438
438
439 // create html for completer
439 // create html for completer
440 var complete = $('<div/>').addClass('completions');
440 var complete = $('<div/>').addClass('completions');
441 complete.attr('id','complete');
441 complete.attr('id','complete');
442 complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
442 complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
443
443
444 var select = $('<select/>').attr('multiple','true');
444 var select = $('<select/>').attr('multiple','true');
445 select.attr('id', 'asyoutypeselect')
445 select.attr('id', 'asyoutypeselect')
446 select.attr('size',Math.min(10,matches.length));
446 select.attr('size',Math.min(10,matches.length));
447 var pos = this.code_mirror.cursorCoords();
447 var pos = this.code_mirror.cursorCoords();
448
448
449 // TODO: I propose to remove enough horizontal pixel
449 // TODO: I propose to remove enough horizontal pixel
450 // to align the text later
450 // to align the text later
451 complete.css('left',pos.x+'px');
451 complete.css('left',pos.x+'px');
452 complete.css('top',pos.yBot+'px');
452 complete.css('top',pos.yBot+'px');
453 complete.append(select);
453 complete.append(select);
454
454
455 $('body').append(complete);
455 $('body').append(complete);
456
456
457 // So a first actual completion. see if all the completion start wit
457 // So a first actual completion. see if all the completion start wit
458 // the same letter and complete if necessary
458 // the same letter and complete if necessary
459 ff = sharedStart(matches)
459 ff = sharedStart(matches)
460 fastForward = ff[0];
460 fastForward = ff[0];
461 completing_to_magic = ff[1];
461 completing_to_magic = ff[1];
462 typed_characters = fastForward.substr(matched_text.length);
462 typed_characters = fastForward.substr(matched_text.length);
463 complete_with(matches,matched_text+typed_characters,true,null);
463 complete_with(matches,matched_text+typed_characters,true,null);
464 filterd = matches;
464 filterd = matches;
465 // Give focus to select, and make it filter the match as the user type
465 // Give focus to select, and make it filter the match as the user type
466 // by filtering the previous matches. Called by .keypress and .keydown
466 // by filtering the previous matches. Called by .keypress and .keydown
467 var downandpress = function (event,press_or_down) {
467 var downandpress = function (event,press_or_down) {
468 var code = event.which;
468 var code = event.which;
469 var autopick = false; // auto 'pick' if only one match
469 var autopick = false; // auto 'pick' if only one match
470 if (press_or_down === 0){
470 if (press_or_down === 0){
471 press = true; down = false; //Are we called from keypress or keydown
471 press = true; down = false; //Are we called from keypress or keydown
472 } else if (press_or_down == 1){
472 } else if (press_or_down == 1){
473 press = false; down = true;
473 press = false; down = true;
474 }
474 }
475 if (code === key.shift) {
475 if (code === key.shift) {
476 // nothing on Shift
476 // nothing on Shift
477 return;
477 return;
478 }
478 }
479 if (key.dismissAndAppend(code) && press) {
479 if (key.dismissAndAppend(code) && press) {
480 var newchar = String.fromCharCode(code);
480 var newchar = String.fromCharCode(code);
481 typed_characters = typed_characters+newchar;
481 typed_characters = typed_characters+newchar;
482 insert(matched_text+typed_characters,event);
482 insert(matched_text+typed_characters,event);
483 return
483 return
484 }
484 }
485 if (code === key.enter) {
485 if (code === key.enter) {
486 // Pressing ENTER will cause a pick
486 // Pressing ENTER will cause a pick
487 event.stopPropagation();
487 event.stopPropagation();
488 event.preventDefault();
488 event.preventDefault();
489 pick();
489 pick();
490 } else if (code === 38 || code === 40) {
490 } else if (code === 38 || code === 40) {
491 // We don't want the document keydown handler to handle UP/DOWN,
491 // We don't want the document keydown handler to handle UP/DOWN,
492 // but we want the default action.
492 // but we want the default action.
493 event.stopPropagation();
493 event.stopPropagation();
494 } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
494 } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
495 if( key.isCompSymbol(code) && press)
495 if( key.isCompSymbol(code) && press)
496 {
496 {
497 var newchar = String.fromCharCode(code);
497 var newchar = String.fromCharCode(code);
498 typed_characters = typed_characters+newchar;
498 typed_characters = typed_characters+newchar;
499 } else if (code == key.tab) {
499 } else if (code == key.tab) {
500 ff = sharedStart(matches)
500 ff = sharedStart(matches)
501 fastForward = ff[0];
501 fastForward = ff[0];
502 completing_to_magic = ff[1];
502 completing_to_magic = ff[1];
503 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
503 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
504 typed_characters = typed_characters+ffsub;
504 typed_characters = typed_characters+ffsub;
505 autopick = true;
505 autopick = true;
506 } else if (code == key.backspace && down) {
506 } else if (code == key.backspace && down) {
507 // cancel if user have erase everything, otherwise decrease
507 // cancel if user have erase everything, otherwise decrease
508 // what we filter with
508 // what we filter with
509 event.preventDefault();
509 event.preventDefault();
510 if (typed_characters.length <= 0)
510 if (typed_characters.length <= 0)
511 {
511 {
512 insert(matched_text,event)
512 insert(matched_text,event)
513 return
513 return
514 }
514 }
515 typed_characters = typed_characters.substr(0,typed_characters.length-1);
515 typed_characters = typed_characters.substr(0,typed_characters.length-1);
516 } else if (press && code != key.backspace && code != key.tab && code != 0){
516 } else if (press && code != key.backspace && code != key.tab && code != 0){
517 insert(matched_text+typed_characters,event);
517 insert(matched_text+typed_characters,event);
518 return
518 return
519 } else {
519 } else {
520 return
520 return
521 }
521 }
522 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
522 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
523 filterd = matches.filter(function(x){return re.test(x)});
523 filterd = matches.filter(function(x){return re.test(x)});
524 ff = sharedStart(filterd);
524 ff = sharedStart(filterd);
525 completing_to_magic = ff[1];
525 completing_to_magic = ff[1];
526 complete_with(filterd,matched_text+typed_characters,autopick,event);
526 complete_with(filterd,matched_text+typed_characters,autopick,event);
527 } else if (code == key.esc) {
527 } else if (code == key.esc) {
528 // dismiss the completer and go back to before invoking it
528 // dismiss the completer and go back to before invoking it
529 insert(matched_text,event);
529 insert(matched_text,event);
530 } else if (press) { // abort only on .keypress or esc
530 } else if (press) { // abort only on .keypress or esc
531 }
531 }
532 }
532 }
533 select.keydown(function (event) {
533 select.keydown(function (event) {
534 downandpress(event,1)
534 downandpress(event,1)
535 });
535 });
536 select.keypress(function (event) {
536 select.keypress(function (event) {
537 downandpress(event,0)
537 downandpress(event,0)
538 });
538 });
539 // Double click also causes a pick.
539 // Double click also causes a pick.
540 // and bind the last actions.
540 // and bind the last actions.
541 select.dblclick(pick);
541 select.dblclick(pick);
542 select.blur(close);
542 select.blur(close);
543 select.focus();
543 select.focus();
544 };
544 };
545
545
546
546
547 CodeCell.prototype.select = function () {
547 CodeCell.prototype.select = function () {
548 IPython.Cell.prototype.select.apply(this);
548 IPython.Cell.prototype.select.apply(this);
549 this.code_mirror.refresh();
549 this.code_mirror.refresh();
550 this.code_mirror.focus();
550 this.code_mirror.focus();
551 // We used to need an additional refresh() after the focus, but
551 // We used to need an additional refresh() after the focus, but
552 // it appears that this has been fixed in CM. This bug would show
552 // it appears that this has been fixed in CM. This bug would show
553 // up on FF when a newly loaded markdown cell was edited.
553 // up on FF when a newly loaded markdown cell was edited.
554 };
554 };
555
555
556
556
557 CodeCell.prototype.select_all = function () {
557 CodeCell.prototype.select_all = function () {
558 var start = {line: 0, ch: 0};
558 var start = {line: 0, ch: 0};
559 var nlines = this.code_mirror.lineCount();
559 var nlines = this.code_mirror.lineCount();
560 var last_line = this.code_mirror.getLine(nlines-1);
560 var last_line = this.code_mirror.getLine(nlines-1);
561 var end = {line: nlines-1, ch: last_line.length};
561 var end = {line: nlines-1, ch: last_line.length};
562 this.code_mirror.setSelection(start, end);
562 this.code_mirror.setSelection(start, end);
563 };
563 };
564
564
565
565
566 CodeCell.prototype.append_output = function (json) {
566 CodeCell.prototype.append_output = function (json, dynamic) {
567 // If dynamic is true, javascript output will be eval'd.
567 this.expand();
568 this.expand();
568 if (json.output_type === 'pyout') {
569 if (json.output_type === 'pyout') {
569 this.append_pyout(json);
570 this.append_pyout(json, dynamic);
570 } else if (json.output_type === 'pyerr') {
571 } else if (json.output_type === 'pyerr') {
571 this.append_pyerr(json);
572 this.append_pyerr(json);
572 } else if (json.output_type === 'display_data') {
573 } else if (json.output_type === 'display_data') {
573 this.append_display_data(json);
574 this.append_display_data(json, dynamic);
574 } else if (json.output_type === 'stream') {
575 } else if (json.output_type === 'stream') {
575 this.append_stream(json);
576 this.append_stream(json);
576 };
577 };
577 this.outputs.push(json);
578 this.outputs.push(json);
578 };
579 };
579
580
580
581
581 CodeCell.prototype.create_output_area = function () {
582 CodeCell.prototype.create_output_area = function () {
582 var oa = $("<div/>").addClass("hbox output_area");
583 var oa = $("<div/>").addClass("hbox output_area");
583 oa.append($('<div/>').addClass('prompt'));
584 oa.append($('<div/>').addClass('prompt'));
584 return oa;
585 return oa;
585 };
586 };
586
587
587
588
588 CodeCell.prototype.append_pyout = function (json) {
589 CodeCell.prototype.append_pyout = function (json, dynamic) {
589 n = json.prompt_number || ' ';
590 n = json.prompt_number || ' ';
590 var toinsert = this.create_output_area();
591 var toinsert = this.create_output_area();
591 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
592 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
592 this.append_mime_type(json, toinsert);
593 this.append_mime_type(json, toinsert, dynamic);
593 this.element.find('div.output').append(toinsert);
594 this.element.find('div.output').append(toinsert);
594 // If we just output latex, typeset it.
595 // If we just output latex, typeset it.
595 if ((json.latex !== undefined) || (json.html !== undefined)) {
596 if ((json.latex !== undefined) || (json.html !== undefined)) {
596 this.typeset();
597 this.typeset();
597 };
598 };
598 };
599 };
599
600
600
601
601 CodeCell.prototype.append_pyerr = function (json) {
602 CodeCell.prototype.append_pyerr = function (json) {
602 var tb = json.traceback;
603 var tb = json.traceback;
603 if (tb !== undefined && tb.length > 0) {
604 if (tb !== undefined && tb.length > 0) {
604 var s = '';
605 var s = '';
605 var len = tb.length;
606 var len = tb.length;
606 for (var i=0; i<len; i++) {
607 for (var i=0; i<len; i++) {
607 s = s + tb[i] + '\n';
608 s = s + tb[i] + '\n';
608 }
609 }
609 s = s + '\n';
610 s = s + '\n';
610 var toinsert = this.create_output_area();
611 var toinsert = this.create_output_area();
611 this.append_text(s, toinsert);
612 this.append_text(s, toinsert);
612 this.element.find('div.output').append(toinsert);
613 this.element.find('div.output').append(toinsert);
613 };
614 };
614 };
615 };
615
616
616
617
617 CodeCell.prototype.append_stream = function (json) {
618 CodeCell.prototype.append_stream = function (json) {
618 // temporary fix: if stream undefined (json file written prior to this patch),
619 // temporary fix: if stream undefined (json file written prior to this patch),
619 // default to most likely stdout:
620 // default to most likely stdout:
620 if (json.stream == undefined){
621 if (json.stream == undefined){
621 json.stream = 'stdout';
622 json.stream = 'stdout';
622 }
623 }
623 var subclass = "output_"+json.stream;
624 var subclass = "output_"+json.stream;
624 if (this.outputs.length > 0){
625 if (this.outputs.length > 0){
625 // have at least one output to consider
626 // have at least one output to consider
626 var last = this.outputs[this.outputs.length-1];
627 var last = this.outputs[this.outputs.length-1];
627 if (last.output_type == 'stream' && json.stream == last.stream){
628 if (last.output_type == 'stream' && json.stream == last.stream){
628 // latest output was in the same stream,
629 // latest output was in the same stream,
629 // so append directly into its pre tag
630 // so append directly into its pre tag
630 // escape ANSI & HTML specials:
631 // escape ANSI & HTML specials:
631 var text = utils.fixConsole(json.text);
632 var text = utils.fixConsole(json.text);
632 this.element.find('div.'+subclass).last().find('pre').append(text);
633 this.element.find('div.'+subclass).last().find('pre').append(text);
633 return;
634 return;
634 }
635 }
635 }
636 }
636
637
637 // If we got here, attach a new div
638 // If we got here, attach a new div
638 var toinsert = this.create_output_area();
639 var toinsert = this.create_output_area();
639 this.append_text(json.text, toinsert, "output_stream "+subclass);
640 this.append_text(json.text, toinsert, "output_stream "+subclass);
640 this.element.find('div.output').append(toinsert);
641 this.element.find('div.output').append(toinsert);
641 };
642 };
642
643
643
644
644 CodeCell.prototype.append_display_data = function (json) {
645 CodeCell.prototype.append_display_data = function (json, dynamic) {
645 var toinsert = this.create_output_area();
646 var toinsert = this.create_output_area();
646 this.append_mime_type(json, toinsert);
647 this.append_mime_type(json, toinsert, dynamic);
647 this.element.find('div.output').append(toinsert);
648 this.element.find('div.output').append(toinsert);
648 // If we just output latex, typeset it.
649 // If we just output latex, typeset it.
649 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
650 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
650 this.typeset();
651 this.typeset();
651 };
652 };
652 };
653 };
653
654
654
655
655 CodeCell.prototype.append_mime_type = function (json, element) {
656 CodeCell.prototype.append_mime_type = function (json, element, dynamic) {
656 if (json.html !== undefined) {
657 if (json.javascript !== undefined && dynamic) {
658 this.append_javascript(json.javascript, element, dynamic);
659 } else if (json.html !== undefined) {
657 this.append_html(json.html, element);
660 this.append_html(json.html, element);
658 } else if (json.javascript !== undefined) {
659 this.append_javascript(json.javascript, element);
660 } else if (json.latex !== undefined) {
661 } else if (json.latex !== undefined) {
661 this.append_latex(json.latex, element);
662 this.append_latex(json.latex, element);
662 } else if (json.svg !== undefined) {
663 } else if (json.svg !== undefined) {
663 this.append_svg(json.svg, element);
664 this.append_svg(json.svg, element);
664 } else if (json.png !== undefined) {
665 } else if (json.png !== undefined) {
665 this.append_png(json.png, element);
666 this.append_png(json.png, element);
666 } else if (json.jpeg !== undefined) {
667 } else if (json.jpeg !== undefined) {
667 this.append_jpeg(json.jpeg, element);
668 this.append_jpeg(json.jpeg, element);
668 } else if (json.text !== undefined) {
669 } else if (json.text !== undefined) {
669 this.append_text(json.text, element);
670 this.append_text(json.text, element);
670 };
671 };
671 };
672 };
672
673
673
674
674 CodeCell.prototype.append_html = function (html, element) {
675 CodeCell.prototype.append_html = function (html, element) {
675 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
676 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
676 toinsert.append(html);
677 toinsert.append(html);
677 element.append(toinsert);
678 element.append(toinsert);
678 };
679 };
679
680
680
681
681 CodeCell.prototype.append_javascript = function (js, element) {
682 CodeCell.prototype.append_javascript = function (js, e) {
682 // We just eval the JS code, element appears in the local scope.
683 // We just eval the JS code, element appears in the local scope.
684 var element = $("<div/>").addClass("box_flex1 output_subarea");
685 e.append(element);
683 eval(js);
686 eval(js);
684 }
687 }
685
688
686
689
687 CodeCell.prototype.append_text = function (data, element, extra_class) {
690 CodeCell.prototype.append_text = function (data, element, extra_class) {
688 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
691 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
689 // escape ANSI & HTML specials in plaintext:
692 // escape ANSI & HTML specials in plaintext:
690 data = utils.fixConsole(data);
693 data = utils.fixConsole(data);
691 if (extra_class){
694 if (extra_class){
692 toinsert.addClass(extra_class);
695 toinsert.addClass(extra_class);
693 }
696 }
694 toinsert.append($("<pre/>").html(data));
697 toinsert.append($("<pre/>").html(data));
695 element.append(toinsert);
698 element.append(toinsert);
696 };
699 };
697
700
698
701
699 CodeCell.prototype.append_svg = function (svg, element) {
702 CodeCell.prototype.append_svg = function (svg, element) {
700 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
703 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
701 toinsert.append(svg);
704 toinsert.append(svg);
702 element.append(toinsert);
705 element.append(toinsert);
703 };
706 };
704
707
705
708
706 CodeCell.prototype.append_png = function (png, element) {
709 CodeCell.prototype.append_png = function (png, element) {
707 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
710 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
708 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
711 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
709 element.append(toinsert);
712 element.append(toinsert);
710 };
713 };
711
714
712
715
713 CodeCell.prototype.append_jpeg = function (jpeg, element) {
716 CodeCell.prototype.append_jpeg = function (jpeg, element) {
714 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
717 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
715 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
718 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
716 element.append(toinsert);
719 element.append(toinsert);
717 };
720 };
718
721
719
722
720 CodeCell.prototype.append_latex = function (latex, element) {
723 CodeCell.prototype.append_latex = function (latex, element) {
721 // This method cannot do the typesetting because the latex first has to
724 // This method cannot do the typesetting because the latex first has to
722 // be on the page.
725 // be on the page.
723 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
726 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
724 toinsert.append(latex);
727 toinsert.append(latex);
725 element.append(toinsert);
728 element.append(toinsert);
726 };
729 };
727
730
728
731
729 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
732 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
730 var output_div = this.element.find("div.output");
733 var output_div = this.element.find("div.output");
731 if (stdout && stderr && other){
734 if (stdout && stderr && other){
732 // clear all, no need for logic
735 // clear all, no need for logic
733 output_div.html("");
736 output_div.html("");
734 this.outputs = [];
737 this.outputs = [];
735 return;
738 return;
736 }
739 }
737 // remove html output
740 // remove html output
738 // each output_subarea that has an identifying class is in an output_area
741 // each output_subarea that has an identifying class is in an output_area
739 // which is the element to be removed.
742 // which is the element to be removed.
740 if (stdout){
743 if (stdout){
741 output_div.find("div.output_stdout").parent().remove();
744 output_div.find("div.output_stdout").parent().remove();
742 }
745 }
743 if (stderr){
746 if (stderr){
744 output_div.find("div.output_stderr").parent().remove();
747 output_div.find("div.output_stderr").parent().remove();
745 }
748 }
746 if (other){
749 if (other){
747 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
750 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
748 }
751 }
749
752
750 // remove cleared outputs from JSON list:
753 // remove cleared outputs from JSON list:
751 for (var i = this.outputs.length - 1; i >= 0; i--){
754 for (var i = this.outputs.length - 1; i >= 0; i--){
752 var out = this.outputs[i];
755 var out = this.outputs[i];
753 var output_type = out.output_type;
756 var output_type = out.output_type;
754 if (output_type == "display_data" && other){
757 if (output_type == "display_data" && other){
755 this.outputs.splice(i,1);
758 this.outputs.splice(i,1);
756 }else if (output_type == "stream"){
759 }else if (output_type == "stream"){
757 if (stdout && out.stream == "stdout"){
760 if (stdout && out.stream == "stdout"){
758 this.outputs.splice(i,1);
761 this.outputs.splice(i,1);
759 }else if (stderr && out.stream == "stderr"){
762 }else if (stderr && out.stream == "stderr"){
760 this.outputs.splice(i,1);
763 this.outputs.splice(i,1);
761 }
764 }
762 }
765 }
763 }
766 }
764 };
767 };
765
768
766
769
767 CodeCell.prototype.clear_input = function () {
770 CodeCell.prototype.clear_input = function () {
768 this.code_mirror.setValue('');
771 this.code_mirror.setValue('');
769 };
772 };
770
773
771
774
772 CodeCell.prototype.collapse = function () {
775 CodeCell.prototype.collapse = function () {
773 if (!this.collapsed) {
776 if (!this.collapsed) {
774 this.element.find('div.output').hide();
777 this.element.find('div.output').hide();
775 this.collapsed = true;
778 this.collapsed = true;
776 };
779 };
777 };
780 };
778
781
779
782
780 CodeCell.prototype.expand = function () {
783 CodeCell.prototype.expand = function () {
781 if (this.collapsed) {
784 if (this.collapsed) {
782 this.element.find('div.output').show();
785 this.element.find('div.output').show();
783 this.collapsed = false;
786 this.collapsed = false;
784 };
787 };
785 };
788 };
786
789
787
790
788 CodeCell.prototype.toggle_output = function () {
791 CodeCell.prototype.toggle_output = function () {
789 if (this.collapsed) {
792 if (this.collapsed) {
790 this.expand();
793 this.expand();
791 } else {
794 } else {
792 this.collapse();
795 this.collapse();
793 };
796 };
794 };
797 };
795
798
796 CodeCell.prototype.set_input_prompt = function (number) {
799 CodeCell.prototype.set_input_prompt = function (number) {
797 this.input_prompt_number = number;
800 this.input_prompt_number = number;
798 var ns = number || "&nbsp;";
801 var ns = number || "&nbsp;";
799 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
802 this.element.find('div.input_prompt').html('In&nbsp;[' + ns + ']:');
800 };
803 };
801
804
802
805
803 CodeCell.prototype.get_text = function () {
806 CodeCell.prototype.get_text = function () {
804 return this.code_mirror.getValue();
807 return this.code_mirror.getValue();
805 };
808 };
806
809
807
810
808 CodeCell.prototype.set_text = function (code) {
811 CodeCell.prototype.set_text = function (code) {
809 return this.code_mirror.setValue(code);
812 return this.code_mirror.setValue(code);
810 };
813 };
811
814
812
815
813 CodeCell.prototype.at_top = function () {
816 CodeCell.prototype.at_top = function () {
814 var cursor = this.code_mirror.getCursor();
817 var cursor = this.code_mirror.getCursor();
815 if (cursor.line === 0) {
818 if (cursor.line === 0) {
816 return true;
819 return true;
817 } else {
820 } else {
818 return false;
821 return false;
819 }
822 }
820 };
823 };
821
824
822
825
823 CodeCell.prototype.at_bottom = function () {
826 CodeCell.prototype.at_bottom = function () {
824 var cursor = this.code_mirror.getCursor();
827 var cursor = this.code_mirror.getCursor();
825 if (cursor.line === (this.code_mirror.lineCount()-1)) {
828 if (cursor.line === (this.code_mirror.lineCount()-1)) {
826 return true;
829 return true;
827 } else {
830 } else {
828 return false;
831 return false;
829 }
832 }
830 };
833 };
831
834
832
835
833 CodeCell.prototype.fromJSON = function (data) {
836 CodeCell.prototype.fromJSON = function (data) {
834 if (data.cell_type === 'code') {
837 if (data.cell_type === 'code') {
835 if (data.input !== undefined) {
838 if (data.input !== undefined) {
836 this.set_text(data.input);
839 this.set_text(data.input);
837 }
840 }
838 if (data.prompt_number !== undefined) {
841 if (data.prompt_number !== undefined) {
839 this.set_input_prompt(data.prompt_number);
842 this.set_input_prompt(data.prompt_number);
840 } else {
843 } else {
841 this.set_input_prompt();
844 this.set_input_prompt();
842 };
845 };
843 var len = data.outputs.length;
846 var len = data.outputs.length;
844 for (var i=0; i<len; i++) {
847 for (var i=0; i<len; i++) {
845 this.append_output(data.outputs[i]);
848 // append with dynamic=false.
849 this.append_output(data.outputs[i], false);
846 };
850 };
847 if (data.collapsed !== undefined) {
851 if (data.collapsed !== undefined) {
848 if (data.collapsed) {
852 if (data.collapsed) {
849 this.collapse();
853 this.collapse();
850 } else {
854 } else {
851 this.expand();
855 this.expand();
852 };
856 };
853 };
857 };
854 };
858 };
855 };
859 };
856
860
857
861
858 CodeCell.prototype.toJSON = function () {
862 CodeCell.prototype.toJSON = function () {
859 var data = {};
863 var data = {};
860 data.input = this.get_text();
864 data.input = this.get_text();
861 data.cell_type = 'code';
865 data.cell_type = 'code';
862 if (this.input_prompt_number) {
866 if (this.input_prompt_number) {
863 data.prompt_number = this.input_prompt_number;
867 data.prompt_number = this.input_prompt_number;
864 };
868 };
865 var outputs = [];
869 var outputs = [];
866 var len = this.outputs.length;
870 var len = this.outputs.length;
867 for (var i=0; i<len; i++) {
871 for (var i=0; i<len; i++) {
868 outputs[i] = this.outputs[i];
872 outputs[i] = this.outputs[i];
869 };
873 };
870 data.outputs = outputs;
874 data.outputs = outputs;
871 data.language = 'python';
875 data.language = 'python';
872 data.collapsed = this.collapsed;
876 data.collapsed = this.collapsed;
873 return data;
877 return data;
874 };
878 };
875
879
876
880
877 IPython.CodeCell = CodeCell;
881 IPython.CodeCell = CodeCell;
878
882
879 return IPython;
883 return IPython;
880 }(IPython));
884 }(IPython));
@@ -1,1341 +1,1342
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15
15
16 var Notebook = function (selector) {
16 var Notebook = function (selector) {
17 this.read_only = IPython.read_only;
17 this.read_only = IPython.read_only;
18 this.element = $(selector);
18 this.element = $(selector);
19 this.element.scroll();
19 this.element.scroll();
20 this.element.data("notebook", this);
20 this.element.data("notebook", this);
21 this.next_prompt_number = 1;
21 this.next_prompt_number = 1;
22 this.kernel = null;
22 this.kernel = null;
23 this.clipboard = null;
23 this.clipboard = null;
24 this.paste_enabled = false;
24 this.paste_enabled = false;
25 this.dirty = false;
25 this.dirty = false;
26 this.msg_cell_map = {};
26 this.msg_cell_map = {};
27 this.metadata = {};
27 this.metadata = {};
28 this.control_key_active = false;
28 this.control_key_active = false;
29 this.notebook_id = null;
29 this.notebook_id = null;
30 this.notebook_name = null;
30 this.notebook_name = null;
31 this.notebook_name_blacklist_re = /[\/\\]/;
31 this.notebook_name_blacklist_re = /[\/\\]/;
32 this.nbformat = 3 // Increment this when changing the nbformat
32 this.nbformat = 3 // Increment this when changing the nbformat
33 this.style();
33 this.style();
34 this.create_elements();
34 this.create_elements();
35 this.bind_events();
35 this.bind_events();
36 this.set_tooltipontab(true);
36 this.set_tooltipontab(true);
37 this.set_smartcompleter(true);
37 this.set_smartcompleter(true);
38 this.set_timebeforetooltip(1200);
38 this.set_timebeforetooltip(1200);
39 };
39 };
40
40
41
41
42 Notebook.prototype.style = function () {
42 Notebook.prototype.style = function () {
43 $('div#notebook').addClass('border-box-sizing');
43 $('div#notebook').addClass('border-box-sizing');
44 };
44 };
45
45
46
46
47 Notebook.prototype.create_elements = function () {
47 Notebook.prototype.create_elements = function () {
48 // We add this end_space div to the end of the notebook div to:
48 // We add this end_space div to the end of the notebook div to:
49 // i) provide a margin between the last cell and the end of the notebook
49 // i) provide a margin between the last cell and the end of the notebook
50 // ii) to prevent the div from scrolling up when the last cell is being
50 // ii) to prevent the div from scrolling up when the last cell is being
51 // edited, but is too low on the page, which browsers will do automatically.
51 // edited, but is too low on the page, which browsers will do automatically.
52 var that = this;
52 var that = this;
53 var end_space = $('<div/>').addClass('end_space').height("30%");
53 var end_space = $('<div/>').addClass('end_space').height("30%");
54 end_space.dblclick(function (e) {
54 end_space.dblclick(function (e) {
55 if (that.read_only) return;
55 if (that.read_only) return;
56 var ncells = that.ncells();
56 var ncells = that.ncells();
57 that.insert_cell_below('code',ncells-1);
57 that.insert_cell_below('code',ncells-1);
58 });
58 });
59 this.element.append(end_space);
59 this.element.append(end_space);
60 $('div#notebook').addClass('border-box-sizing');
60 $('div#notebook').addClass('border-box-sizing');
61 };
61 };
62
62
63
63
64 Notebook.prototype.bind_events = function () {
64 Notebook.prototype.bind_events = function () {
65 var that = this;
65 var that = this;
66 $(document).keydown(function (event) {
66 $(document).keydown(function (event) {
67 // console.log(event);
67 // console.log(event);
68 if (that.read_only) return true;
68 if (that.read_only) return true;
69
69
70 // Save (CTRL+S) or (AppleKey+S)
70 // Save (CTRL+S) or (AppleKey+S)
71 //metaKey = applekey on mac
71 //metaKey = applekey on mac
72 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
72 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
73 that.save_notebook();
73 that.save_notebook();
74 event.preventDefault();
74 event.preventDefault();
75 return false;
75 return false;
76 } else if (event.which === 27) {
76 } else if (event.which === 27) {
77 // Intercept escape at highest level to avoid closing
77 // Intercept escape at highest level to avoid closing
78 // websocket connection with firefox
78 // websocket connection with firefox
79 event.preventDefault();
79 event.preventDefault();
80 }
80 }
81 if (event.which === 38 && !event.shiftKey) {
81 if (event.which === 38 && !event.shiftKey) {
82 var cell = that.get_selected_cell();
82 var cell = that.get_selected_cell();
83 if (cell.at_top()) {
83 if (cell.at_top()) {
84 event.preventDefault();
84 event.preventDefault();
85 that.select_prev();
85 that.select_prev();
86 };
86 };
87 } else if (event.which === 40 && !event.shiftKey) {
87 } else if (event.which === 40 && !event.shiftKey) {
88 var cell = that.get_selected_cell();
88 var cell = that.get_selected_cell();
89 if (cell.at_bottom()) {
89 if (cell.at_bottom()) {
90 event.preventDefault();
90 event.preventDefault();
91 that.select_next();
91 that.select_next();
92 };
92 };
93 } else if (event.which === 13 && event.shiftKey) {
93 } else if (event.which === 13 && event.shiftKey) {
94 that.execute_selected_cell();
94 that.execute_selected_cell();
95 return false;
95 return false;
96 } else if (event.which === 13 && event.ctrlKey) {
96 } else if (event.which === 13 && event.ctrlKey) {
97 that.execute_selected_cell({terminal:true});
97 that.execute_selected_cell({terminal:true});
98 return false;
98 return false;
99 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
99 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
100 that.control_key_active = true;
100 that.control_key_active = true;
101 return false;
101 return false;
102 } else if (event.which === 88 && that.control_key_active) {
102 } else if (event.which === 88 && that.control_key_active) {
103 // Cut selected cell = x
103 // Cut selected cell = x
104 that.cut_cell();
104 that.cut_cell();
105 that.control_key_active = false;
105 that.control_key_active = false;
106 return false;
106 return false;
107 } else if (event.which === 67 && that.control_key_active) {
107 } else if (event.which === 67 && that.control_key_active) {
108 // Copy selected cell = c
108 // Copy selected cell = c
109 that.copy_cell();
109 that.copy_cell();
110 that.control_key_active = false;
110 that.control_key_active = false;
111 return false;
111 return false;
112 } else if (event.which === 86 && that.control_key_active) {
112 } else if (event.which === 86 && that.control_key_active) {
113 // Paste selected cell = v
113 // Paste selected cell = v
114 that.paste_cell();
114 that.paste_cell();
115 that.control_key_active = false;
115 that.control_key_active = false;
116 return false;
116 return false;
117 } else if (event.which === 68 && that.control_key_active) {
117 } else if (event.which === 68 && that.control_key_active) {
118 // Delete selected cell = d
118 // Delete selected cell = d
119 that.delete_cell();
119 that.delete_cell();
120 that.control_key_active = false;
120 that.control_key_active = false;
121 return false;
121 return false;
122 } else if (event.which === 65 && that.control_key_active) {
122 } else if (event.which === 65 && that.control_key_active) {
123 // Insert code cell above selected = a
123 // Insert code cell above selected = a
124 that.insert_cell_above('code');
124 that.insert_cell_above('code');
125 that.control_key_active = false;
125 that.control_key_active = false;
126 return false;
126 return false;
127 } else if (event.which === 66 && that.control_key_active) {
127 } else if (event.which === 66 && that.control_key_active) {
128 // Insert code cell below selected = b
128 // Insert code cell below selected = b
129 that.insert_cell_below('code');
129 that.insert_cell_below('code');
130 that.control_key_active = false;
130 that.control_key_active = false;
131 return false;
131 return false;
132 } else if (event.which === 89 && that.control_key_active) {
132 } else if (event.which === 89 && that.control_key_active) {
133 // To code = y
133 // To code = y
134 that.to_code();
134 that.to_code();
135 that.control_key_active = false;
135 that.control_key_active = false;
136 return false;
136 return false;
137 } else if (event.which === 77 && that.control_key_active) {
137 } else if (event.which === 77 && that.control_key_active) {
138 // To markdown = m
138 // To markdown = m
139 that.to_markdown();
139 that.to_markdown();
140 that.control_key_active = false;
140 that.control_key_active = false;
141 return false;
141 return false;
142 } else if (event.which === 84 && that.control_key_active) {
142 } else if (event.which === 84 && that.control_key_active) {
143 // To Plaintext = t
143 // To Plaintext = t
144 that.to_plaintext();
144 that.to_plaintext();
145 that.control_key_active = false;
145 that.control_key_active = false;
146 return false;
146 return false;
147 } else if (event.which === 49 && that.control_key_active) {
147 } else if (event.which === 49 && that.control_key_active) {
148 // To Heading 1 = 1
148 // To Heading 1 = 1
149 that.to_heading(undefined, 1);
149 that.to_heading(undefined, 1);
150 that.control_key_active = false;
150 that.control_key_active = false;
151 return false;
151 return false;
152 } else if (event.which === 50 && that.control_key_active) {
152 } else if (event.which === 50 && that.control_key_active) {
153 // To Heading 2 = 2
153 // To Heading 2 = 2
154 that.to_heading(undefined, 2);
154 that.to_heading(undefined, 2);
155 that.control_key_active = false;
155 that.control_key_active = false;
156 return false;
156 return false;
157 } else if (event.which === 51 && that.control_key_active) {
157 } else if (event.which === 51 && that.control_key_active) {
158 // To Heading 3 = 3
158 // To Heading 3 = 3
159 that.to_heading(undefined, 3);
159 that.to_heading(undefined, 3);
160 that.control_key_active = false;
160 that.control_key_active = false;
161 return false;
161 return false;
162 } else if (event.which === 52 && that.control_key_active) {
162 } else if (event.which === 52 && that.control_key_active) {
163 // To Heading 4 = 4
163 // To Heading 4 = 4
164 that.to_heading(undefined, 4);
164 that.to_heading(undefined, 4);
165 that.control_key_active = false;
165 that.control_key_active = false;
166 return false;
166 return false;
167 } else if (event.which === 53 && that.control_key_active) {
167 } else if (event.which === 53 && that.control_key_active) {
168 // To Heading 5 = 5
168 // To Heading 5 = 5
169 that.to_heading(undefined, 5);
169 that.to_heading(undefined, 5);
170 that.control_key_active = false;
170 that.control_key_active = false;
171 return false;
171 return false;
172 } else if (event.which === 54 && that.control_key_active) {
172 } else if (event.which === 54 && that.control_key_active) {
173 // To Heading 6 = 6
173 // To Heading 6 = 6
174 that.to_heading(undefined, 6);
174 that.to_heading(undefined, 6);
175 that.control_key_active = false;
175 that.control_key_active = false;
176 return false;
176 return false;
177 } else if (event.which === 79 && that.control_key_active) {
177 } else if (event.which === 79 && that.control_key_active) {
178 // Toggle output = o
178 // Toggle output = o
179 that.toggle_output();
179 that.toggle_output();
180 that.control_key_active = false;
180 that.control_key_active = false;
181 return false;
181 return false;
182 } else if (event.which === 83 && that.control_key_active) {
182 } else if (event.which === 83 && that.control_key_active) {
183 // Save notebook = s
183 // Save notebook = s
184 that.save_notebook();
184 that.save_notebook();
185 that.control_key_active = false;
185 that.control_key_active = false;
186 return false;
186 return false;
187 } else if (event.which === 74 && that.control_key_active) {
187 } else if (event.which === 74 && that.control_key_active) {
188 // Move cell down = j
188 // Move cell down = j
189 that.move_cell_down();
189 that.move_cell_down();
190 that.control_key_active = false;
190 that.control_key_active = false;
191 return false;
191 return false;
192 } else if (event.which === 75 && that.control_key_active) {
192 } else if (event.which === 75 && that.control_key_active) {
193 // Move cell up = k
193 // Move cell up = k
194 that.move_cell_up();
194 that.move_cell_up();
195 that.control_key_active = false;
195 that.control_key_active = false;
196 return false;
196 return false;
197 } else if (event.which === 80 && that.control_key_active) {
197 } else if (event.which === 80 && that.control_key_active) {
198 // Select previous = p
198 // Select previous = p
199 that.select_prev();
199 that.select_prev();
200 that.control_key_active = false;
200 that.control_key_active = false;
201 return false;
201 return false;
202 } else if (event.which === 78 && that.control_key_active) {
202 } else if (event.which === 78 && that.control_key_active) {
203 // Select next = n
203 // Select next = n
204 that.select_next();
204 that.select_next();
205 that.control_key_active = false;
205 that.control_key_active = false;
206 return false;
206 return false;
207 } else if (event.which === 76 && that.control_key_active) {
207 } else if (event.which === 76 && that.control_key_active) {
208 // Toggle line numbers = l
208 // Toggle line numbers = l
209 that.cell_toggle_line_numbers();
209 that.cell_toggle_line_numbers();
210 that.control_key_active = false;
210 that.control_key_active = false;
211 return false;
211 return false;
212 } else if (event.which === 73 && that.control_key_active) {
212 } else if (event.which === 73 && that.control_key_active) {
213 // Interrupt kernel = i
213 // Interrupt kernel = i
214 that.kernel.interrupt();
214 that.kernel.interrupt();
215 that.control_key_active = false;
215 that.control_key_active = false;
216 return false;
216 return false;
217 } else if (event.which === 190 && that.control_key_active) {
217 } else if (event.which === 190 && that.control_key_active) {
218 // Restart kernel = . # matches qt console
218 // Restart kernel = . # matches qt console
219 that.restart_kernel();
219 that.restart_kernel();
220 that.control_key_active = false;
220 that.control_key_active = false;
221 return false;
221 return false;
222 } else if (event.which === 72 && that.control_key_active) {
222 } else if (event.which === 72 && that.control_key_active) {
223 // Show keyboard shortcuts = h
223 // Show keyboard shortcuts = h
224 IPython.quick_help.show_keyboard_shortcuts();
224 IPython.quick_help.show_keyboard_shortcuts();
225 that.control_key_active = false;
225 that.control_key_active = false;
226 return false;
226 return false;
227 } else if (that.control_key_active) {
227 } else if (that.control_key_active) {
228 that.control_key_active = false;
228 that.control_key_active = false;
229 return true;
229 return true;
230 };
230 };
231 return true;
231 return true;
232 });
232 });
233
233
234 this.element.bind('collapse_pager', function () {
234 this.element.bind('collapse_pager', function () {
235 var app_height = $('div#main_app').height(); // content height
235 var app_height = $('div#main_app').height(); // content height
236 var splitter_height = $('div#pager_splitter').outerHeight(true);
236 var splitter_height = $('div#pager_splitter').outerHeight(true);
237 var new_height = app_height - splitter_height;
237 var new_height = app_height - splitter_height;
238 that.element.animate({height : new_height + 'px'}, 'fast');
238 that.element.animate({height : new_height + 'px'}, 'fast');
239 });
239 });
240
240
241 this.element.bind('expand_pager', function () {
241 this.element.bind('expand_pager', function () {
242 var app_height = $('div#main_app').height(); // content height
242 var app_height = $('div#main_app').height(); // content height
243 var splitter_height = $('div#pager_splitter').outerHeight(true);
243 var splitter_height = $('div#pager_splitter').outerHeight(true);
244 var pager_height = $('div#pager').outerHeight(true);
244 var pager_height = $('div#pager').outerHeight(true);
245 var new_height = app_height - pager_height - splitter_height;
245 var new_height = app_height - pager_height - splitter_height;
246 that.element.animate({height : new_height + 'px'}, 'fast');
246 that.element.animate({height : new_height + 'px'}, 'fast');
247 });
247 });
248
248
249 $(window).bind('beforeunload', function () {
249 $(window).bind('beforeunload', function () {
250 // TODO: Make killing the kernel configurable.
250 // TODO: Make killing the kernel configurable.
251 var kill_kernel = false;
251 var kill_kernel = false;
252 if (kill_kernel) {
252 if (kill_kernel) {
253 that.kernel.kill();
253 that.kernel.kill();
254 }
254 }
255 if (that.dirty && ! that.read_only) {
255 if (that.dirty && ! that.read_only) {
256 return "You have unsaved changes that will be lost if you leave this page.";
256 return "You have unsaved changes that will be lost if you leave this page.";
257 };
257 };
258 // Null is the *only* return value that will make the browser not
258 // Null is the *only* return value that will make the browser not
259 // pop up the "don't leave" dialog.
259 // pop up the "don't leave" dialog.
260 return null;
260 return null;
261 });
261 });
262 };
262 };
263
263
264
264
265 Notebook.prototype.scroll_to_bottom = function () {
265 Notebook.prototype.scroll_to_bottom = function () {
266 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
266 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
267 };
267 };
268
268
269
269
270 Notebook.prototype.scroll_to_top = function () {
270 Notebook.prototype.scroll_to_top = function () {
271 this.element.animate({scrollTop:0}, 0);
271 this.element.animate({scrollTop:0}, 0);
272 };
272 };
273
273
274
274
275 // Cell indexing, retrieval, etc.
275 // Cell indexing, retrieval, etc.
276
276
277 Notebook.prototype.get_cell_elements = function () {
277 Notebook.prototype.get_cell_elements = function () {
278 return this.element.children("div.cell");
278 return this.element.children("div.cell");
279 };
279 };
280
280
281
281
282 Notebook.prototype.get_cell_element = function (index) {
282 Notebook.prototype.get_cell_element = function (index) {
283 var result = null;
283 var result = null;
284 var e = this.get_cell_elements().eq(index);
284 var e = this.get_cell_elements().eq(index);
285 if (e.length !== 0) {
285 if (e.length !== 0) {
286 result = e;
286 result = e;
287 }
287 }
288 return result;
288 return result;
289 };
289 };
290
290
291
291
292 Notebook.prototype.ncells = function (cell) {
292 Notebook.prototype.ncells = function (cell) {
293 return this.get_cell_elements().length;
293 return this.get_cell_elements().length;
294 };
294 };
295
295
296
296
297 // TODO: we are often calling cells as cells()[i], which we should optimize
297 // TODO: we are often calling cells as cells()[i], which we should optimize
298 // to cells(i) or a new method.
298 // to cells(i) or a new method.
299 Notebook.prototype.get_cells = function () {
299 Notebook.prototype.get_cells = function () {
300 return this.get_cell_elements().toArray().map(function (e) {
300 return this.get_cell_elements().toArray().map(function (e) {
301 return $(e).data("cell");
301 return $(e).data("cell");
302 });
302 });
303 };
303 };
304
304
305
305
306 Notebook.prototype.get_cell = function (index) {
306 Notebook.prototype.get_cell = function (index) {
307 var result = null;
307 var result = null;
308 var ce = this.get_cell_element(index);
308 var ce = this.get_cell_element(index);
309 if (ce !== null) {
309 if (ce !== null) {
310 result = ce.data('cell');
310 result = ce.data('cell');
311 }
311 }
312 return result;
312 return result;
313 }
313 }
314
314
315
315
316 Notebook.prototype.get_next_cell = function (cell) {
316 Notebook.prototype.get_next_cell = function (cell) {
317 var result = null;
317 var result = null;
318 var index = this.find_cell_index(cell);
318 var index = this.find_cell_index(cell);
319 if (index !== null && index < this.ncells()) {
319 if (index !== null && index < this.ncells()) {
320 result = this.get_cell(index+1);
320 result = this.get_cell(index+1);
321 }
321 }
322 return result;
322 return result;
323 }
323 }
324
324
325
325
326 Notebook.prototype.get_prev_cell = function (cell) {
326 Notebook.prototype.get_prev_cell = function (cell) {
327 var result = null;
327 var result = null;
328 var index = this.find_cell_index(cell);
328 var index = this.find_cell_index(cell);
329 if (index !== null && index > 1) {
329 if (index !== null && index > 1) {
330 result = this.get_cell(index-1);
330 result = this.get_cell(index-1);
331 }
331 }
332 return result;
332 return result;
333 }
333 }
334
334
335 Notebook.prototype.find_cell_index = function (cell) {
335 Notebook.prototype.find_cell_index = function (cell) {
336 var result = null;
336 var result = null;
337 this.get_cell_elements().filter(function (index) {
337 this.get_cell_elements().filter(function (index) {
338 if ($(this).data("cell") === cell) {
338 if ($(this).data("cell") === cell) {
339 result = index;
339 result = index;
340 };
340 };
341 });
341 });
342 return result;
342 return result;
343 };
343 };
344
344
345
345
346 Notebook.prototype.index_or_selected = function (index) {
346 Notebook.prototype.index_or_selected = function (index) {
347 var i;
347 var i;
348 if (index === undefined || index === null) {
348 if (index === undefined || index === null) {
349 i = this.get_selected_index();
349 i = this.get_selected_index();
350 if (i === null) {
350 if (i === null) {
351 i = 0;
351 i = 0;
352 }
352 }
353 } else {
353 } else {
354 i = index;
354 i = index;
355 }
355 }
356 return i;
356 return i;
357 };
357 };
358
358
359
359
360 Notebook.prototype.get_selected_cell = function () {
360 Notebook.prototype.get_selected_cell = function () {
361 var index = this.get_selected_index();
361 var index = this.get_selected_index();
362 return this.get_cell(index);
362 return this.get_cell(index);
363 };
363 };
364
364
365
365
366 Notebook.prototype.is_valid_cell_index = function (index) {
366 Notebook.prototype.is_valid_cell_index = function (index) {
367 if (index !== null && index >= 0 && index < this.ncells()) {
367 if (index !== null && index >= 0 && index < this.ncells()) {
368 return true;
368 return true;
369 } else {
369 } else {
370 return false;
370 return false;
371 };
371 };
372 }
372 }
373
373
374 Notebook.prototype.get_selected_index = function () {
374 Notebook.prototype.get_selected_index = function () {
375 var result = null;
375 var result = null;
376 this.get_cell_elements().filter(function (index) {
376 this.get_cell_elements().filter(function (index) {
377 if ($(this).data("cell").selected === true) {
377 if ($(this).data("cell").selected === true) {
378 result = index;
378 result = index;
379 };
379 };
380 });
380 });
381 return result;
381 return result;
382 };
382 };
383
383
384
384
385 Notebook.prototype.cell_for_msg = function (msg_id) {
385 Notebook.prototype.cell_for_msg = function (msg_id) {
386 var cell_id = this.msg_cell_map[msg_id];
386 var cell_id = this.msg_cell_map[msg_id];
387 var result = null;
387 var result = null;
388 this.get_cell_elements().filter(function (index) {
388 this.get_cell_elements().filter(function (index) {
389 cell = $(this).data("cell");
389 cell = $(this).data("cell");
390 if (cell.cell_id === cell_id) {
390 if (cell.cell_id === cell_id) {
391 result = cell;
391 result = cell;
392 };
392 };
393 });
393 });
394 return result;
394 return result;
395 };
395 };
396
396
397
397
398 // Cell selection.
398 // Cell selection.
399
399
400 Notebook.prototype.select = function (index) {
400 Notebook.prototype.select = function (index) {
401 if (index !== undefined && index >= 0 && index < this.ncells()) {
401 if (index !== undefined && index >= 0 && index < this.ncells()) {
402 sindex = this.get_selected_index()
402 sindex = this.get_selected_index()
403 if (sindex !== null && index !== sindex) {
403 if (sindex !== null && index !== sindex) {
404 this.get_cell(sindex).unselect();
404 this.get_cell(sindex).unselect();
405 };
405 };
406 var cell = this.get_cell(index)
406 var cell = this.get_cell(index)
407 cell.select();
407 cell.select();
408 if (cell.cell_type === 'heading') {
408 if (cell.cell_type === 'heading') {
409 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
409 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
410 {'cell_type':cell.cell_type,level:cell.level}
410 {'cell_type':cell.cell_type,level:cell.level}
411 );
411 );
412 } else {
412 } else {
413 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
413 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
414 {'cell_type':cell.cell_type}
414 {'cell_type':cell.cell_type}
415 );
415 );
416 };
416 };
417 };
417 };
418 return this;
418 return this;
419 };
419 };
420
420
421
421
422 Notebook.prototype.select_next = function () {
422 Notebook.prototype.select_next = function () {
423 var index = this.get_selected_index();
423 var index = this.get_selected_index();
424 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
424 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
425 this.select(index+1);
425 this.select(index+1);
426 };
426 };
427 return this;
427 return this;
428 };
428 };
429
429
430
430
431 Notebook.prototype.select_prev = function () {
431 Notebook.prototype.select_prev = function () {
432 var index = this.get_selected_index();
432 var index = this.get_selected_index();
433 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
433 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
434 this.select(index-1);
434 this.select(index-1);
435 };
435 };
436 return this;
436 return this;
437 };
437 };
438
438
439
439
440 // Cell movement
440 // Cell movement
441
441
442 Notebook.prototype.move_cell_up = function (index) {
442 Notebook.prototype.move_cell_up = function (index) {
443 var i = this.index_or_selected();
443 var i = this.index_or_selected();
444 if (i !== null && i < this.ncells() && i > 0) {
444 if (i !== null && i < this.ncells() && i > 0) {
445 var pivot = this.get_cell_element(i-1);
445 var pivot = this.get_cell_element(i-1);
446 var tomove = this.get_cell_element(i);
446 var tomove = this.get_cell_element(i);
447 if (pivot !== null && tomove !== null) {
447 if (pivot !== null && tomove !== null) {
448 tomove.detach();
448 tomove.detach();
449 pivot.before(tomove);
449 pivot.before(tomove);
450 this.select(i-1);
450 this.select(i-1);
451 };
451 };
452 };
452 };
453 this.dirty = true;
453 this.dirty = true;
454 return this;
454 return this;
455 };
455 };
456
456
457
457
458 Notebook.prototype.move_cell_down = function (index) {
458 Notebook.prototype.move_cell_down = function (index) {
459 var i = this.index_or_selected();
459 var i = this.index_or_selected();
460 if (i !== null && i < (this.ncells()-1) && i >= 0) {
460 if (i !== null && i < (this.ncells()-1) && i >= 0) {
461 var pivot = this.get_cell_element(i+1);
461 var pivot = this.get_cell_element(i+1);
462 var tomove = this.get_cell_element(i);
462 var tomove = this.get_cell_element(i);
463 if (pivot !== null && tomove !== null) {
463 if (pivot !== null && tomove !== null) {
464 tomove.detach();
464 tomove.detach();
465 pivot.after(tomove);
465 pivot.after(tomove);
466 this.select(i+1);
466 this.select(i+1);
467 };
467 };
468 };
468 };
469 this.dirty = true;
469 this.dirty = true;
470 return this;
470 return this;
471 };
471 };
472
472
473
473
474 Notebook.prototype.sort_cells = function () {
474 Notebook.prototype.sort_cells = function () {
475 // This is not working right now. Calling this will actually crash
475 // This is not working right now. Calling this will actually crash
476 // the browser. I think there is an infinite loop in here...
476 // the browser. I think there is an infinite loop in here...
477 var ncells = this.ncells();
477 var ncells = this.ncells();
478 var sindex = this.get_selected_index();
478 var sindex = this.get_selected_index();
479 var swapped;
479 var swapped;
480 do {
480 do {
481 swapped = false;
481 swapped = false;
482 for (var i=1; i<ncells; i++) {
482 for (var i=1; i<ncells; i++) {
483 current = this.get_cell(i);
483 current = this.get_cell(i);
484 previous = this.get_cell(i-1);
484 previous = this.get_cell(i-1);
485 if (previous.input_prompt_number > current.input_prompt_number) {
485 if (previous.input_prompt_number > current.input_prompt_number) {
486 this.move_cell_up(i);
486 this.move_cell_up(i);
487 swapped = true;
487 swapped = true;
488 };
488 };
489 };
489 };
490 } while (swapped);
490 } while (swapped);
491 this.select(sindex);
491 this.select(sindex);
492 return this;
492 return this;
493 };
493 };
494
494
495 // Insertion, deletion.
495 // Insertion, deletion.
496
496
497 Notebook.prototype.delete_cell = function (index) {
497 Notebook.prototype.delete_cell = function (index) {
498 var i = this.index_or_selected(index);
498 var i = this.index_or_selected(index);
499 if (this.is_valid_cell_index(i)) {
499 if (this.is_valid_cell_index(i)) {
500 var ce = this.get_cell_element(i);
500 var ce = this.get_cell_element(i);
501 ce.remove();
501 ce.remove();
502 if (i === (this.ncells())) {
502 if (i === (this.ncells())) {
503 this.select(i-1);
503 this.select(i-1);
504 } else {
504 } else {
505 this.select(i);
505 this.select(i);
506 };
506 };
507 this.dirty = true;
507 this.dirty = true;
508 };
508 };
509 return this;
509 return this;
510 };
510 };
511
511
512
512
513 Notebook.prototype.insert_cell_below = function (type, index) {
513 Notebook.prototype.insert_cell_below = function (type, index) {
514 // type = ('code','html','markdown')
514 // type = ('code','html','markdown')
515 // index = cell index or undefined to insert below selected
515 // index = cell index or undefined to insert below selected
516 index = this.index_or_selected(index);
516 index = this.index_or_selected(index);
517 var cell = null;
517 var cell = null;
518 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
518 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
519 if (type === 'code') {
519 if (type === 'code') {
520 cell = new IPython.CodeCell(this);
520 cell = new IPython.CodeCell(this);
521 cell.set_input_prompt();
521 cell.set_input_prompt();
522 } else if (type === 'markdown') {
522 } else if (type === 'markdown') {
523 cell = new IPython.MarkdownCell(this);
523 cell = new IPython.MarkdownCell(this);
524 } else if (type === 'html') {
524 } else if (type === 'html') {
525 cell = new IPython.HTMLCell(this);
525 cell = new IPython.HTMLCell(this);
526 } else if (type === 'plaintext') {
526 } else if (type === 'plaintext') {
527 cell = new IPython.PlaintextCell(this);
527 cell = new IPython.PlaintextCell(this);
528 } else if (type === 'heading') {
528 } else if (type === 'heading') {
529 cell = new IPython.HeadingCell(this);
529 cell = new IPython.HeadingCell(this);
530 };
530 };
531 if (cell !== null) {
531 if (cell !== null) {
532 if (this.ncells() === 0) {
532 if (this.ncells() === 0) {
533 this.element.find('div.end_space').before(cell.element);
533 this.element.find('div.end_space').before(cell.element);
534 } else if (this.is_valid_cell_index(index)) {
534 } else if (this.is_valid_cell_index(index)) {
535 this.get_cell_element(index).after(cell.element);
535 this.get_cell_element(index).after(cell.element);
536 };
536 };
537 cell.render();
537 cell.render();
538 this.select(this.find_cell_index(cell));
538 this.select(this.find_cell_index(cell));
539 this.dirty = true;
539 this.dirty = true;
540 return cell;
540 return cell;
541 };
541 };
542 };
542 };
543 return cell;
543 return cell;
544 };
544 };
545
545
546
546
547 Notebook.prototype.insert_cell_above = function (type, index) {
547 Notebook.prototype.insert_cell_above = function (type, index) {
548 // type = ('code','html','markdown')
548 // type = ('code','html','markdown')
549 // index = cell index or undefined to insert above selected
549 // index = cell index or undefined to insert above selected
550 index = this.index_or_selected(index);
550 index = this.index_or_selected(index);
551 var cell = null;
551 var cell = null;
552 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
552 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
553 if (type === 'code') {
553 if (type === 'code') {
554 cell = new IPython.CodeCell(this);
554 cell = new IPython.CodeCell(this);
555 cell.set_input_prompt();
555 cell.set_input_prompt();
556 } else if (type === 'markdown') {
556 } else if (type === 'markdown') {
557 cell = new IPython.MarkdownCell(this);
557 cell = new IPython.MarkdownCell(this);
558 } else if (type === 'html') {
558 } else if (type === 'html') {
559 cell = new IPython.HTMLCell(this);
559 cell = new IPython.HTMLCell(this);
560 } else if (type === 'plaintext') {
560 } else if (type === 'plaintext') {
561 cell = new IPython.PlaintextCell(this);
561 cell = new IPython.PlaintextCell(this);
562 } else if (type === 'heading') {
562 } else if (type === 'heading') {
563 cell = new IPython.HeadingCell(this);
563 cell = new IPython.HeadingCell(this);
564 };
564 };
565 if (cell !== null) {
565 if (cell !== null) {
566 if (this.ncells() === 0) {
566 if (this.ncells() === 0) {
567 this.element.find('div.end_space').before(cell.element);
567 this.element.find('div.end_space').before(cell.element);
568 } else if (this.is_valid_cell_index(index)) {
568 } else if (this.is_valid_cell_index(index)) {
569 this.get_cell_element(index).before(cell.element);
569 this.get_cell_element(index).before(cell.element);
570 };
570 };
571 cell.render();
571 cell.render();
572 this.select(this.find_cell_index(cell));
572 this.select(this.find_cell_index(cell));
573 this.dirty = true;
573 this.dirty = true;
574 return cell;
574 return cell;
575 };
575 };
576 };
576 };
577 return cell;
577 return cell;
578 };
578 };
579
579
580
580
581 Notebook.prototype.to_code = function (index) {
581 Notebook.prototype.to_code = function (index) {
582 var i = this.index_or_selected(index);
582 var i = this.index_or_selected(index);
583 if (this.is_valid_cell_index(i)) {
583 if (this.is_valid_cell_index(i)) {
584 var source_element = this.get_cell_element(i);
584 var source_element = this.get_cell_element(i);
585 var source_cell = source_element.data("cell");
585 var source_cell = source_element.data("cell");
586 if (!(source_cell instanceof IPython.CodeCell)) {
586 if (!(source_cell instanceof IPython.CodeCell)) {
587 target_cell = this.insert_cell_below('code',i);
587 target_cell = this.insert_cell_below('code',i);
588 var text = source_cell.get_text();
588 var text = source_cell.get_text();
589 if (text === source_cell.placeholder) {
589 if (text === source_cell.placeholder) {
590 text = '';
590 text = '';
591 }
591 }
592 target_cell.set_text(text);
592 target_cell.set_text(text);
593 source_element.remove();
593 source_element.remove();
594 this.dirty = true;
594 this.dirty = true;
595 };
595 };
596 };
596 };
597 };
597 };
598
598
599
599
600 Notebook.prototype.to_markdown = function (index) {
600 Notebook.prototype.to_markdown = function (index) {
601 var i = this.index_or_selected(index);
601 var i = this.index_or_selected(index);
602 if (this.is_valid_cell_index(i)) {
602 if (this.is_valid_cell_index(i)) {
603 var source_element = this.get_cell_element(i);
603 var source_element = this.get_cell_element(i);
604 var source_cell = source_element.data("cell");
604 var source_cell = source_element.data("cell");
605 if (!(source_cell instanceof IPython.MarkdownCell)) {
605 if (!(source_cell instanceof IPython.MarkdownCell)) {
606 target_cell = this.insert_cell_below('markdown',i);
606 target_cell = this.insert_cell_below('markdown',i);
607 var text = source_cell.get_text();
607 var text = source_cell.get_text();
608 if (text === source_cell.placeholder) {
608 if (text === source_cell.placeholder) {
609 text = '';
609 text = '';
610 };
610 };
611 // The edit must come before the set_text.
611 // The edit must come before the set_text.
612 target_cell.edit();
612 target_cell.edit();
613 target_cell.set_text(text);
613 target_cell.set_text(text);
614 source_element.remove();
614 source_element.remove();
615 this.dirty = true;
615 this.dirty = true;
616 };
616 };
617 };
617 };
618 };
618 };
619
619
620
620
621 Notebook.prototype.to_html = function (index) {
621 Notebook.prototype.to_html = function (index) {
622 var i = this.index_or_selected(index);
622 var i = this.index_or_selected(index);
623 if (this.is_valid_cell_index(i)) {
623 if (this.is_valid_cell_index(i)) {
624 var source_element = this.get_cell_element(i);
624 var source_element = this.get_cell_element(i);
625 var source_cell = source_element.data("cell");
625 var source_cell = source_element.data("cell");
626 var target_cell = null;
626 var target_cell = null;
627 if (!(source_cell instanceof IPython.HTMLCell)) {
627 if (!(source_cell instanceof IPython.HTMLCell)) {
628 target_cell = this.insert_cell_below('html',i);
628 target_cell = this.insert_cell_below('html',i);
629 var text = source_cell.get_text();
629 var text = source_cell.get_text();
630 if (text === source_cell.placeholder) {
630 if (text === source_cell.placeholder) {
631 text = '';
631 text = '';
632 };
632 };
633 // The edit must come before the set_text.
633 // The edit must come before the set_text.
634 target_cell.edit();
634 target_cell.edit();
635 target_cell.set_text(text);
635 target_cell.set_text(text);
636 source_element.remove();
636 source_element.remove();
637 this.dirty = true;
637 this.dirty = true;
638 };
638 };
639 };
639 };
640 };
640 };
641
641
642
642
643 Notebook.prototype.to_plaintext = function (index) {
643 Notebook.prototype.to_plaintext = function (index) {
644 var i = this.index_or_selected(index);
644 var i = this.index_or_selected(index);
645 if (this.is_valid_cell_index(i)) {
645 if (this.is_valid_cell_index(i)) {
646 var source_element = this.get_cell_element(i);
646 var source_element = this.get_cell_element(i);
647 var source_cell = source_element.data("cell");
647 var source_cell = source_element.data("cell");
648 var target_cell = null;
648 var target_cell = null;
649 if (!(source_cell instanceof IPython.PlaintextCell)) {
649 if (!(source_cell instanceof IPython.PlaintextCell)) {
650 target_cell = this.insert_cell_below('plaintext',i);
650 target_cell = this.insert_cell_below('plaintext',i);
651 var text = source_cell.get_text();
651 var text = source_cell.get_text();
652 if (text === source_cell.placeholder) {
652 if (text === source_cell.placeholder) {
653 text = '';
653 text = '';
654 };
654 };
655 // The edit must come before the set_text.
655 // The edit must come before the set_text.
656 target_cell.edit();
656 target_cell.edit();
657 target_cell.set_text(text);
657 target_cell.set_text(text);
658 source_element.remove();
658 source_element.remove();
659 this.dirty = true;
659 this.dirty = true;
660 };
660 };
661 };
661 };
662 };
662 };
663
663
664
664
665 Notebook.prototype.to_heading = function (index, level) {
665 Notebook.prototype.to_heading = function (index, level) {
666 level = level || 1;
666 level = level || 1;
667 var i = this.index_or_selected(index);
667 var i = this.index_or_selected(index);
668 if (this.is_valid_cell_index(i)) {
668 if (this.is_valid_cell_index(i)) {
669 var source_element = this.get_cell_element(i);
669 var source_element = this.get_cell_element(i);
670 var source_cell = source_element.data("cell");
670 var source_cell = source_element.data("cell");
671 var target_cell = null;
671 var target_cell = null;
672 if (source_cell instanceof IPython.HeadingCell) {
672 if (source_cell instanceof IPython.HeadingCell) {
673 source_cell.set_level(level);
673 source_cell.set_level(level);
674 } else {
674 } else {
675 target_cell = this.insert_cell_below('heading',i);
675 target_cell = this.insert_cell_below('heading',i);
676 var text = source_cell.get_text();
676 var text = source_cell.get_text();
677 if (text === source_cell.placeholder) {
677 if (text === source_cell.placeholder) {
678 text = '';
678 text = '';
679 };
679 };
680 // The edit must come before the set_text.
680 // The edit must come before the set_text.
681 target_cell.set_level(level);
681 target_cell.set_level(level);
682 target_cell.edit();
682 target_cell.edit();
683 target_cell.set_text(text);
683 target_cell.set_text(text);
684 source_element.remove();
684 source_element.remove();
685 this.dirty = true;
685 this.dirty = true;
686 };
686 };
687 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
687 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
688 {'cell_type':'heading',level:level}
688 {'cell_type':'heading',level:level}
689 );
689 );
690 };
690 };
691 };
691 };
692
692
693
693
694 // Cut/Copy/Paste
694 // Cut/Copy/Paste
695
695
696 Notebook.prototype.enable_paste = function () {
696 Notebook.prototype.enable_paste = function () {
697 var that = this;
697 var that = this;
698 if (!this.paste_enabled) {
698 if (!this.paste_enabled) {
699 $('#paste_cell').removeClass('ui-state-disabled')
699 $('#paste_cell').removeClass('ui-state-disabled')
700 .on('click', function () {that.paste_cell();});
700 .on('click', function () {that.paste_cell();});
701 $('#paste_cell_above').removeClass('ui-state-disabled')
701 $('#paste_cell_above').removeClass('ui-state-disabled')
702 .on('click', function () {that.paste_cell_above();});
702 .on('click', function () {that.paste_cell_above();});
703 $('#paste_cell_below').removeClass('ui-state-disabled')
703 $('#paste_cell_below').removeClass('ui-state-disabled')
704 .on('click', function () {that.paste_cell_below();});
704 .on('click', function () {that.paste_cell_below();});
705 this.paste_enabled = true;
705 this.paste_enabled = true;
706 };
706 };
707 };
707 };
708
708
709
709
710 Notebook.prototype.disable_paste = function () {
710 Notebook.prototype.disable_paste = function () {
711 if (this.paste_enabled) {
711 if (this.paste_enabled) {
712 $('#paste_cell').addClass('ui-state-disabled').off('click');
712 $('#paste_cell').addClass('ui-state-disabled').off('click');
713 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
713 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
714 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
714 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
715 this.paste_enabled = false;
715 this.paste_enabled = false;
716 };
716 };
717 };
717 };
718
718
719
719
720 Notebook.prototype.cut_cell = function () {
720 Notebook.prototype.cut_cell = function () {
721 this.copy_cell();
721 this.copy_cell();
722 this.delete_cell();
722 this.delete_cell();
723 }
723 }
724
724
725 Notebook.prototype.copy_cell = function () {
725 Notebook.prototype.copy_cell = function () {
726 var cell = this.get_selected_cell();
726 var cell = this.get_selected_cell();
727 this.clipboard = cell.toJSON();
727 this.clipboard = cell.toJSON();
728 this.enable_paste();
728 this.enable_paste();
729 };
729 };
730
730
731
731
732 Notebook.prototype.paste_cell = function () {
732 Notebook.prototype.paste_cell = function () {
733 if (this.clipboard !== null && this.paste_enabled) {
733 if (this.clipboard !== null && this.paste_enabled) {
734 var cell_data = this.clipboard;
734 var cell_data = this.clipboard;
735 var new_cell = this.insert_cell_above(cell_data.cell_type);
735 var new_cell = this.insert_cell_above(cell_data.cell_type);
736 new_cell.fromJSON(cell_data);
736 new_cell.fromJSON(cell_data);
737 old_cell = this.get_next_cell(new_cell);
737 old_cell = this.get_next_cell(new_cell);
738 this.delete_cell(this.find_cell_index(old_cell));
738 this.delete_cell(this.find_cell_index(old_cell));
739 this.select(this.find_cell_index(new_cell));
739 this.select(this.find_cell_index(new_cell));
740 };
740 };
741 };
741 };
742
742
743
743
744 Notebook.prototype.paste_cell_above = function () {
744 Notebook.prototype.paste_cell_above = function () {
745 if (this.clipboard !== null && this.paste_enabled) {
745 if (this.clipboard !== null && this.paste_enabled) {
746 var cell_data = this.clipboard;
746 var cell_data = this.clipboard;
747 var new_cell = this.insert_cell_above(cell_data.cell_type);
747 var new_cell = this.insert_cell_above(cell_data.cell_type);
748 new_cell.fromJSON(cell_data);
748 new_cell.fromJSON(cell_data);
749 };
749 };
750 };
750 };
751
751
752
752
753 Notebook.prototype.paste_cell_below = function () {
753 Notebook.prototype.paste_cell_below = function () {
754 if (this.clipboard !== null && this.paste_enabled) {
754 if (this.clipboard !== null && this.paste_enabled) {
755 var cell_data = this.clipboard;
755 var cell_data = this.clipboard;
756 var new_cell = this.insert_cell_below(cell_data.cell_type);
756 var new_cell = this.insert_cell_below(cell_data.cell_type);
757 new_cell.fromJSON(cell_data);
757 new_cell.fromJSON(cell_data);
758 };
758 };
759 };
759 };
760
760
761
761
762 // Split/merge
762 // Split/merge
763
763
764 Notebook.prototype.split_cell = function () {
764 Notebook.prototype.split_cell = function () {
765 // Todo: implement spliting for other cell types.
765 // Todo: implement spliting for other cell types.
766 var cell = this.get_selected_cell();
766 var cell = this.get_selected_cell();
767 if (cell.is_splittable()) {
767 if (cell.is_splittable()) {
768 texta = cell.get_pre_cursor();
768 texta = cell.get_pre_cursor();
769 textb = cell.get_post_cursor();
769 textb = cell.get_post_cursor();
770 if (cell instanceof IPython.CodeCell) {
770 if (cell instanceof IPython.CodeCell) {
771 cell.set_text(texta);
771 cell.set_text(texta);
772 var new_cell = this.insert_cell_below('code');
772 var new_cell = this.insert_cell_below('code');
773 new_cell.set_text(textb);
773 new_cell.set_text(textb);
774 } else if (cell instanceof IPython.MarkdownCell) {
774 } else if (cell instanceof IPython.MarkdownCell) {
775 cell.set_text(texta);
775 cell.set_text(texta);
776 cell.render();
776 cell.render();
777 var new_cell = this.insert_cell_below('markdown');
777 var new_cell = this.insert_cell_below('markdown');
778 new_cell.edit(); // editor must be visible to call set_text
778 new_cell.edit(); // editor must be visible to call set_text
779 new_cell.set_text(textb);
779 new_cell.set_text(textb);
780 new_cell.render();
780 new_cell.render();
781 } else if (cell instanceof IPython.HTMLCell) {
781 } else if (cell instanceof IPython.HTMLCell) {
782 cell.set_text(texta);
782 cell.set_text(texta);
783 cell.render();
783 cell.render();
784 var new_cell = this.insert_cell_below('html');
784 var new_cell = this.insert_cell_below('html');
785 new_cell.edit(); // editor must be visible to call set_text
785 new_cell.edit(); // editor must be visible to call set_text
786 new_cell.set_text(textb);
786 new_cell.set_text(textb);
787 new_cell.render();
787 new_cell.render();
788 };
788 };
789 };
789 };
790 };
790 };
791
791
792
792
793 Notebook.prototype.merge_cell_above = function () {
793 Notebook.prototype.merge_cell_above = function () {
794 var index = this.get_selected_index();
794 var index = this.get_selected_index();
795 var cell = this.get_cell(index);
795 var cell = this.get_cell(index);
796 if (index > 0) {
796 if (index > 0) {
797 upper_cell = this.get_cell(index-1);
797 upper_cell = this.get_cell(index-1);
798 upper_text = upper_cell.get_text();
798 upper_text = upper_cell.get_text();
799 text = cell.get_text();
799 text = cell.get_text();
800 if (cell instanceof IPython.CodeCell) {
800 if (cell instanceof IPython.CodeCell) {
801 cell.set_text(upper_text+'\n'+text);
801 cell.set_text(upper_text+'\n'+text);
802 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
802 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
803 cell.edit();
803 cell.edit();
804 cell.set_text(upper_text+'\n'+text);
804 cell.set_text(upper_text+'\n'+text);
805 cell.render();
805 cell.render();
806 };
806 };
807 this.delete_cell(index-1);
807 this.delete_cell(index-1);
808 this.select(this.find_cell_index(cell));
808 this.select(this.find_cell_index(cell));
809 };
809 };
810 };
810 };
811
811
812
812
813 Notebook.prototype.merge_cell_below = function () {
813 Notebook.prototype.merge_cell_below = function () {
814 var index = this.get_selected_index();
814 var index = this.get_selected_index();
815 var cell = this.get_cell(index);
815 var cell = this.get_cell(index);
816 if (index < this.ncells()-1) {
816 if (index < this.ncells()-1) {
817 lower_cell = this.get_cell(index+1);
817 lower_cell = this.get_cell(index+1);
818 lower_text = lower_cell.get_text();
818 lower_text = lower_cell.get_text();
819 text = cell.get_text();
819 text = cell.get_text();
820 if (cell instanceof IPython.CodeCell) {
820 if (cell instanceof IPython.CodeCell) {
821 cell.set_text(text+'\n'+lower_text);
821 cell.set_text(text+'\n'+lower_text);
822 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
822 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
823 cell.edit();
823 cell.edit();
824 cell.set_text(text+'\n'+lower_text);
824 cell.set_text(text+'\n'+lower_text);
825 cell.render();
825 cell.render();
826 };
826 };
827 this.delete_cell(index+1);
827 this.delete_cell(index+1);
828 this.select(this.find_cell_index(cell));
828 this.select(this.find_cell_index(cell));
829 };
829 };
830 };
830 };
831
831
832
832
833 // Cell collapsing and output clearing
833 // Cell collapsing and output clearing
834
834
835 Notebook.prototype.collapse = function (index) {
835 Notebook.prototype.collapse = function (index) {
836 var i = this.index_or_selected(index);
836 var i = this.index_or_selected(index);
837 this.get_cell(i).collapse();
837 this.get_cell(i).collapse();
838 this.dirty = true;
838 this.dirty = true;
839 };
839 };
840
840
841
841
842 Notebook.prototype.expand = function (index) {
842 Notebook.prototype.expand = function (index) {
843 var i = this.index_or_selected(index);
843 var i = this.index_or_selected(index);
844 this.get_cell(i).expand();
844 this.get_cell(i).expand();
845 this.dirty = true;
845 this.dirty = true;
846 };
846 };
847
847
848
848
849 Notebook.prototype.toggle_output = function (index) {
849 Notebook.prototype.toggle_output = function (index) {
850 var i = this.index_or_selected(index);
850 var i = this.index_or_selected(index);
851 this.get_cell(i).toggle_output();
851 this.get_cell(i).toggle_output();
852 this.dirty = true;
852 this.dirty = true;
853 };
853 };
854
854
855
855
856 Notebook.prototype.set_timebeforetooltip = function (time) {
856 Notebook.prototype.set_timebeforetooltip = function (time) {
857 this.time_before_tooltip = time;
857 this.time_before_tooltip = time;
858 };
858 };
859
859
860
860
861 Notebook.prototype.set_tooltipontab = function (state) {
861 Notebook.prototype.set_tooltipontab = function (state) {
862 this.tooltip_on_tab = state;
862 this.tooltip_on_tab = state;
863 };
863 };
864
864
865
865
866 Notebook.prototype.set_smartcompleter = function (state) {
866 Notebook.prototype.set_smartcompleter = function (state) {
867 this.smart_completer = state;
867 this.smart_completer = state;
868 };
868 };
869
869
870
870
871 Notebook.prototype.clear_all_output = function () {
871 Notebook.prototype.clear_all_output = function () {
872 var ncells = this.ncells();
872 var ncells = this.ncells();
873 var cells = this.get_cells();
873 var cells = this.get_cells();
874 for (var i=0; i<ncells; i++) {
874 for (var i=0; i<ncells; i++) {
875 if (cells[i] instanceof IPython.CodeCell) {
875 if (cells[i] instanceof IPython.CodeCell) {
876 cells[i].clear_output(true,true,true);
876 cells[i].clear_output(true,true,true);
877 }
877 }
878 };
878 };
879 this.dirty = true;
879 this.dirty = true;
880 };
880 };
881
881
882
882
883 // Other cell functions: line numbers, ...
883 // Other cell functions: line numbers, ...
884
884
885 Notebook.prototype.cell_toggle_line_numbers = function() {
885 Notebook.prototype.cell_toggle_line_numbers = function() {
886 this.get_selected_cell().toggle_line_numbers();
886 this.get_selected_cell().toggle_line_numbers();
887 };
887 };
888
888
889 // Kernel related things
889 // Kernel related things
890
890
891 Notebook.prototype.start_kernel = function () {
891 Notebook.prototype.start_kernel = function () {
892 this.kernel = new IPython.Kernel();
892 this.kernel = new IPython.Kernel();
893 this.kernel.start(this.notebook_id, $.proxy(this.kernel_started, this));
893 this.kernel.start(this.notebook_id, $.proxy(this.kernel_started, this));
894 };
894 };
895
895
896
896
897 Notebook.prototype.restart_kernel = function () {
897 Notebook.prototype.restart_kernel = function () {
898 var that = this;
898 var that = this;
899 var dialog = $('<div/>');
899 var dialog = $('<div/>');
900 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
900 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
901 $(document).append(dialog);
901 $(document).append(dialog);
902 dialog.dialog({
902 dialog.dialog({
903 resizable: false,
903 resizable: false,
904 modal: true,
904 modal: true,
905 title: "Restart kernel or continue running?",
905 title: "Restart kernel or continue running?",
906 closeText: '',
906 closeText: '',
907 buttons : {
907 buttons : {
908 "Restart": function () {
908 "Restart": function () {
909 that.kernel.restart($.proxy(that.kernel_started, that));
909 that.kernel.restart($.proxy(that.kernel_started, that));
910 $(this).dialog('close');
910 $(this).dialog('close');
911 },
911 },
912 "Continue running": function () {
912 "Continue running": function () {
913 $(this).dialog('close');
913 $(this).dialog('close');
914 }
914 }
915 }
915 }
916 });
916 });
917 };
917 };
918
918
919
919
920 Notebook.prototype.kernel_started = function () {
920 Notebook.prototype.kernel_started = function () {
921 console.log("Kernel started: ", this.kernel.kernel_id);
921 console.log("Kernel started: ", this.kernel.kernel_id);
922 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
922 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
923 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
923 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
924 };
924 };
925
925
926
926
927 Notebook.prototype.handle_shell_reply = function (e) {
927 Notebook.prototype.handle_shell_reply = function (e) {
928 reply = $.parseJSON(e.data);
928 reply = $.parseJSON(e.data);
929 var header = reply.header;
929 var header = reply.header;
930 var content = reply.content;
930 var content = reply.content;
931 var msg_type = header.msg_type;
931 var msg_type = header.msg_type;
932 // console.log(reply);
932 // console.log(reply);
933 var cell = this.cell_for_msg(reply.parent_header.msg_id);
933 var cell = this.cell_for_msg(reply.parent_header.msg_id);
934 if (msg_type === "execute_reply") {
934 if (msg_type === "execute_reply") {
935 cell.set_input_prompt(content.execution_count);
935 cell.set_input_prompt(content.execution_count);
936 cell.element.removeClass("running");
936 cell.element.removeClass("running");
937 this.dirty = true;
937 this.dirty = true;
938 } else if (msg_type === "complete_reply") {
938 } else if (msg_type === "complete_reply") {
939 cell.finish_completing(content.matched_text, content.matches);
939 cell.finish_completing(content.matched_text, content.matches);
940 } else if (msg_type === "object_info_reply"){
940 } else if (msg_type === "object_info_reply"){
941 //console.log('back from object_info_request : ')
941 //console.log('back from object_info_request : ')
942 rep = reply.content;
942 rep = reply.content;
943 if(rep.found)
943 if(rep.found)
944 {
944 {
945 cell.finish_tooltip(rep);
945 cell.finish_tooltip(rep);
946 }
946 }
947 } else {
947 } else {
948 //console.log("unknown reply:"+msg_type);
948 //console.log("unknown reply:"+msg_type);
949 }
949 }
950 // when having a rely from object_info_reply,
950 // when having a rely from object_info_reply,
951 // no payload so no nned to handle it
951 // no payload so no nned to handle it
952 if(typeof(content.payload)!='undefined') {
952 if(typeof(content.payload)!='undefined') {
953 var payload = content.payload || [];
953 var payload = content.payload || [];
954 this.handle_payload(cell, payload);
954 this.handle_payload(cell, payload);
955 }
955 }
956 };
956 };
957
957
958
958
959 Notebook.prototype.handle_payload = function (cell, payload) {
959 Notebook.prototype.handle_payload = function (cell, payload) {
960 var l = payload.length;
960 var l = payload.length;
961 for (var i=0; i<l; i++) {
961 for (var i=0; i<l; i++) {
962 if (payload[i].source === 'IPython.zmq.page.page') {
962 if (payload[i].source === 'IPython.zmq.page.page') {
963 if (payload[i].text.trim() !== '') {
963 if (payload[i].text.trim() !== '') {
964 IPython.pager.clear();
964 IPython.pager.clear();
965 IPython.pager.expand();
965 IPython.pager.expand();
966 IPython.pager.append_text(payload[i].text);
966 IPython.pager.append_text(payload[i].text);
967 }
967 }
968 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
968 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
969 var index = this.find_cell_index(cell);
969 var index = this.find_cell_index(cell);
970 var new_cell = this.insert_cell_below('code',index);
970 var new_cell = this.insert_cell_below('code',index);
971 new_cell.set_text(payload[i].text);
971 new_cell.set_text(payload[i].text);
972 this.dirty = true;
972 this.dirty = true;
973 }
973 }
974 };
974 };
975 };
975 };
976
976
977
977
978 Notebook.prototype.handle_iopub_reply = function (e) {
978 Notebook.prototype.handle_iopub_reply = function (e) {
979 reply = $.parseJSON(e.data);
979 reply = $.parseJSON(e.data);
980 var content = reply.content;
980 var content = reply.content;
981 // console.log(reply);
981 // console.log(reply);
982 var msg_type = reply.header.msg_type;
982 var msg_type = reply.header.msg_type;
983 var cell = this.cell_for_msg(reply.parent_header.msg_id);
983 var cell = this.cell_for_msg(reply.parent_header.msg_id);
984 if (msg_type !== 'status' && !cell){
984 if (msg_type !== 'status' && !cell){
985 // message not from this notebook, but should be attached to a cell
985 // message not from this notebook, but should be attached to a cell
986 // console.log("Received IOPub message not caused by one of my cells");
986 // console.log("Received IOPub message not caused by one of my cells");
987 // console.log(reply);
987 // console.log(reply);
988 return;
988 return;
989 }
989 }
990 var output_types = ['stream','display_data','pyout','pyerr'];
990 var output_types = ['stream','display_data','pyout','pyerr'];
991 if (output_types.indexOf(msg_type) >= 0) {
991 if (output_types.indexOf(msg_type) >= 0) {
992 this.handle_output(cell, msg_type, content);
992 this.handle_output(cell, msg_type, content);
993 } else if (msg_type === 'status') {
993 } else if (msg_type === 'status') {
994 if (content.execution_state === 'busy') {
994 if (content.execution_state === 'busy') {
995 $([IPython.events]).trigger('status_busy.Kernel');
995 $([IPython.events]).trigger('status_busy.Kernel');
996 } else if (content.execution_state === 'idle') {
996 } else if (content.execution_state === 'idle') {
997 $([IPython.events]).trigger('status_idle.Kernel');
997 $([IPython.events]).trigger('status_idle.Kernel');
998 } else if (content.execution_state === 'dead') {
998 } else if (content.execution_state === 'dead') {
999 this.handle_status_dead();
999 this.handle_status_dead();
1000 };
1000 };
1001 } else if (msg_type === 'clear_output') {
1001 } else if (msg_type === 'clear_output') {
1002 cell.clear_output(content.stdout, content.stderr, content.other);
1002 cell.clear_output(content.stdout, content.stderr, content.other);
1003 };
1003 };
1004 };
1004 };
1005
1005
1006
1006
1007 Notebook.prototype.handle_status_dead = function () {
1007 Notebook.prototype.handle_status_dead = function () {
1008 var that = this;
1008 var that = this;
1009 this.kernel.stop_channels();
1009 this.kernel.stop_channels();
1010 var dialog = $('<div/>');
1010 var dialog = $('<div/>');
1011 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
1011 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
1012 $(document).append(dialog);
1012 $(document).append(dialog);
1013 dialog.dialog({
1013 dialog.dialog({
1014 resizable: false,
1014 resizable: false,
1015 modal: true,
1015 modal: true,
1016 title: "Dead kernel",
1016 title: "Dead kernel",
1017 buttons : {
1017 buttons : {
1018 "Restart": function () {
1018 "Restart": function () {
1019 that.start_kernel();
1019 that.start_kernel();
1020 $(this).dialog('close');
1020 $(this).dialog('close');
1021 },
1021 },
1022 "Continue running": function () {
1022 "Continue running": function () {
1023 $(this).dialog('close');
1023 $(this).dialog('close');
1024 }
1024 }
1025 }
1025 }
1026 });
1026 });
1027 };
1027 };
1028
1028
1029
1029
1030 Notebook.prototype.handle_output = function (cell, msg_type, content) {
1030 Notebook.prototype.handle_output = function (cell, msg_type, content) {
1031 var json = {};
1031 var json = {};
1032 json.output_type = msg_type;
1032 json.output_type = msg_type;
1033 if (msg_type === "stream") {
1033 if (msg_type === "stream") {
1034 json.text = content.data;
1034 json.text = content.data;
1035 json.stream = content.name;
1035 json.stream = content.name;
1036 } else if (msg_type === "display_data") {
1036 } else if (msg_type === "display_data") {
1037 json = this.convert_mime_types(json, content.data);
1037 json = this.convert_mime_types(json, content.data);
1038 } else if (msg_type === "pyout") {
1038 } else if (msg_type === "pyout") {
1039 json.prompt_number = content.execution_count;
1039 json.prompt_number = content.execution_count;
1040 json = this.convert_mime_types(json, content.data);
1040 json = this.convert_mime_types(json, content.data);
1041 } else if (msg_type === "pyerr") {
1041 } else if (msg_type === "pyerr") {
1042 json.ename = content.ename;
1042 json.ename = content.ename;
1043 json.evalue = content.evalue;
1043 json.evalue = content.evalue;
1044 json.traceback = content.traceback;
1044 json.traceback = content.traceback;
1045 };
1045 };
1046 cell.append_output(json);
1046 // append with dynamic=true
1047 cell.append_output(json, true);
1047 this.dirty = true;
1048 this.dirty = true;
1048 };
1049 };
1049
1050
1050
1051
1051 Notebook.prototype.convert_mime_types = function (json, data) {
1052 Notebook.prototype.convert_mime_types = function (json, data) {
1052 if (data['text/plain'] !== undefined) {
1053 if (data['text/plain'] !== undefined) {
1053 json.text = data['text/plain'];
1054 json.text = data['text/plain'];
1054 };
1055 };
1055 if (data['text/html'] !== undefined) {
1056 if (data['text/html'] !== undefined) {
1056 json.html = data['text/html'];
1057 json.html = data['text/html'];
1057 };
1058 };
1058 if (data['image/svg+xml'] !== undefined) {
1059 if (data['image/svg+xml'] !== undefined) {
1059 json.svg = data['image/svg+xml'];
1060 json.svg = data['image/svg+xml'];
1060 };
1061 };
1061 if (data['image/png'] !== undefined) {
1062 if (data['image/png'] !== undefined) {
1062 json.png = data['image/png'];
1063 json.png = data['image/png'];
1063 };
1064 };
1064 if (data['image/jpeg'] !== undefined) {
1065 if (data['image/jpeg'] !== undefined) {
1065 json.jpeg = data['image/jpeg'];
1066 json.jpeg = data['image/jpeg'];
1066 };
1067 };
1067 if (data['text/latex'] !== undefined) {
1068 if (data['text/latex'] !== undefined) {
1068 json.latex = data['text/latex'];
1069 json.latex = data['text/latex'];
1069 };
1070 };
1070 if (data['application/json'] !== undefined) {
1071 if (data['application/json'] !== undefined) {
1071 json.json = data['application/json'];
1072 json.json = data['application/json'];
1072 };
1073 };
1073 if (data['application/javascript'] !== undefined) {
1074 if (data['application/javascript'] !== undefined) {
1074 json.javascript = data['application/javascript'];
1075 json.javascript = data['application/javascript'];
1075 }
1076 }
1076 return json;
1077 return json;
1077 };
1078 };
1078
1079
1079
1080
1080 Notebook.prototype.execute_selected_cell = function (options) {
1081 Notebook.prototype.execute_selected_cell = function (options) {
1081 // add_new: should a new cell be added if we are at the end of the nb
1082 // add_new: should a new cell be added if we are at the end of the nb
1082 // terminal: execute in terminal mode, which stays in the current cell
1083 // terminal: execute in terminal mode, which stays in the current cell
1083 default_options = {terminal: false, add_new: true};
1084 default_options = {terminal: false, add_new: true};
1084 $.extend(default_options, options);
1085 $.extend(default_options, options);
1085 var that = this;
1086 var that = this;
1086 var cell = that.get_selected_cell();
1087 var cell = that.get_selected_cell();
1087 var cell_index = that.find_cell_index(cell);
1088 var cell_index = that.find_cell_index(cell);
1088 if (cell instanceof IPython.CodeCell) {
1089 if (cell instanceof IPython.CodeCell) {
1089 cell.clear_output(true, true, true);
1090 cell.clear_output(true, true, true);
1090 cell.set_input_prompt('*');
1091 cell.set_input_prompt('*');
1091 cell.element.addClass("running");
1092 cell.element.addClass("running");
1092 var code = cell.get_text();
1093 var code = cell.get_text();
1093 var msg_id = that.kernel.execute(cell.get_text());
1094 var msg_id = that.kernel.execute(cell.get_text());
1094 that.msg_cell_map[msg_id] = cell.cell_id;
1095 that.msg_cell_map[msg_id] = cell.cell_id;
1095 } else if (cell instanceof IPython.HTMLCell) {
1096 } else if (cell instanceof IPython.HTMLCell) {
1096 cell.render();
1097 cell.render();
1097 }
1098 }
1098 if (default_options.terminal) {
1099 if (default_options.terminal) {
1099 cell.select_all();
1100 cell.select_all();
1100 } else {
1101 } else {
1101 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1102 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1102 that.insert_cell_below('code');
1103 that.insert_cell_below('code');
1103 // If we are adding a new cell at the end, scroll down to show it.
1104 // If we are adding a new cell at the end, scroll down to show it.
1104 that.scroll_to_bottom();
1105 that.scroll_to_bottom();
1105 } else {
1106 } else {
1106 that.select(cell_index+1);
1107 that.select(cell_index+1);
1107 };
1108 };
1108 };
1109 };
1109 this.dirty = true;
1110 this.dirty = true;
1110 };
1111 };
1111
1112
1112
1113
1113 Notebook.prototype.execute_all_cells = function () {
1114 Notebook.prototype.execute_all_cells = function () {
1114 var ncells = this.ncells();
1115 var ncells = this.ncells();
1115 for (var i=0; i<ncells; i++) {
1116 for (var i=0; i<ncells; i++) {
1116 this.select(i);
1117 this.select(i);
1117 this.execute_selected_cell({add_new:false});
1118 this.execute_selected_cell({add_new:false});
1118 };
1119 };
1119 this.scroll_to_bottom();
1120 this.scroll_to_bottom();
1120 };
1121 };
1121
1122
1122
1123
1123 Notebook.prototype.request_tool_tip = function (cell,func) {
1124 Notebook.prototype.request_tool_tip = function (cell,func) {
1124 // Feel free to shorten this logic if you are better
1125 // Feel free to shorten this logic if you are better
1125 // than me in regEx
1126 // than me in regEx
1126 // basicaly you shoul be able to get xxx.xxx.xxx from
1127 // basicaly you shoul be able to get xxx.xxx.xxx from
1127 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
1128 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
1128 // remove everything between matchin bracket (need to iterate)
1129 // remove everything between matchin bracket (need to iterate)
1129 matchBracket = /\([^\(\)]+\)/g;
1130 matchBracket = /\([^\(\)]+\)/g;
1130 oldfunc = func;
1131 oldfunc = func;
1131 func = func.replace(matchBracket,"");
1132 func = func.replace(matchBracket,"");
1132 while( oldfunc != func )
1133 while( oldfunc != func )
1133 {
1134 {
1134 oldfunc = func;
1135 oldfunc = func;
1135 func = func.replace(matchBracket,"");
1136 func = func.replace(matchBracket,"");
1136 }
1137 }
1137 // remove everythin after last open bracket
1138 // remove everythin after last open bracket
1138 endBracket = /\([^\(]*$/g;
1139 endBracket = /\([^\(]*$/g;
1139 func = func.replace(endBracket,"");
1140 func = func.replace(endBracket,"");
1140 var re = /[a-zA-Z._]+$/g;
1141 var re = /[a-zA-Z._]+$/g;
1141 var msg_id = this.kernel.object_info_request(re.exec(func));
1142 var msg_id = this.kernel.object_info_request(re.exec(func));
1142 if(typeof(msg_id)!='undefined'){
1143 if(typeof(msg_id)!='undefined'){
1143 this.msg_cell_map[msg_id] = cell.cell_id;
1144 this.msg_cell_map[msg_id] = cell.cell_id;
1144 }
1145 }
1145 };
1146 };
1146
1147
1147 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
1148 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
1148 var msg_id = this.kernel.complete(line, cursor_pos);
1149 var msg_id = this.kernel.complete(line, cursor_pos);
1149 this.msg_cell_map[msg_id] = cell.cell_id;
1150 this.msg_cell_map[msg_id] = cell.cell_id;
1150 };
1151 };
1151
1152
1152
1153
1153 // Persistance and loading
1154 // Persistance and loading
1154
1155
1155 Notebook.prototype.get_notebook_id = function () {
1156 Notebook.prototype.get_notebook_id = function () {
1156 return this.notebook_id;
1157 return this.notebook_id;
1157 };
1158 };
1158
1159
1159
1160
1160 Notebook.prototype.get_notebook_name = function () {
1161 Notebook.prototype.get_notebook_name = function () {
1161 return this.notebook_name;
1162 return this.notebook_name;
1162 };
1163 };
1163
1164
1164
1165
1165 Notebook.prototype.set_notebook_name = function (name) {
1166 Notebook.prototype.set_notebook_name = function (name) {
1166 this.notebook_name = name;
1167 this.notebook_name = name;
1167 };
1168 };
1168
1169
1169
1170
1170 Notebook.prototype.test_notebook_name = function (nbname) {
1171 Notebook.prototype.test_notebook_name = function (nbname) {
1171 nbname = nbname || '';
1172 nbname = nbname || '';
1172 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1173 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1173 return true;
1174 return true;
1174 } else {
1175 } else {
1175 return false;
1176 return false;
1176 };
1177 };
1177 };
1178 };
1178
1179
1179
1180
1180 Notebook.prototype.fromJSON = function (data) {
1181 Notebook.prototype.fromJSON = function (data) {
1181 var ncells = this.ncells();
1182 var ncells = this.ncells();
1182 var i;
1183 var i;
1183 for (i=0; i<ncells; i++) {
1184 for (i=0; i<ncells; i++) {
1184 // Always delete cell 0 as they get renumbered as they are deleted.
1185 // Always delete cell 0 as they get renumbered as they are deleted.
1185 this.delete_cell(0);
1186 this.delete_cell(0);
1186 };
1187 };
1187 // Save the metadata and name.
1188 // Save the metadata and name.
1188 this.metadata = data.metadata;
1189 this.metadata = data.metadata;
1189 this.notebook_name = data.metadata.name;
1190 this.notebook_name = data.metadata.name;
1190 // Only handle 1 worksheet for now.
1191 // Only handle 1 worksheet for now.
1191 var worksheet = data.worksheets[0];
1192 var worksheet = data.worksheets[0];
1192 if (worksheet !== undefined) {
1193 if (worksheet !== undefined) {
1193 var new_cells = worksheet.cells;
1194 var new_cells = worksheet.cells;
1194 ncells = new_cells.length;
1195 ncells = new_cells.length;
1195 var cell_data = null;
1196 var cell_data = null;
1196 var new_cell = null;
1197 var new_cell = null;
1197 for (i=0; i<ncells; i++) {
1198 for (i=0; i<ncells; i++) {
1198 cell_data = new_cells[i];
1199 cell_data = new_cells[i];
1199 new_cell = this.insert_cell_below(cell_data.cell_type);
1200 new_cell = this.insert_cell_below(cell_data.cell_type);
1200 new_cell.fromJSON(cell_data);
1201 new_cell.fromJSON(cell_data);
1201 };
1202 };
1202 };
1203 };
1203 };
1204 };
1204
1205
1205
1206
1206 Notebook.prototype.toJSON = function () {
1207 Notebook.prototype.toJSON = function () {
1207 var cells = this.get_cells();
1208 var cells = this.get_cells();
1208 var ncells = cells.length;
1209 var ncells = cells.length;
1209 cell_array = new Array(ncells);
1210 cell_array = new Array(ncells);
1210 for (var i=0; i<ncells; i++) {
1211 for (var i=0; i<ncells; i++) {
1211 cell_array[i] = cells[i].toJSON();
1212 cell_array[i] = cells[i].toJSON();
1212 };
1213 };
1213 data = {
1214 data = {
1214 // Only handle 1 worksheet for now.
1215 // Only handle 1 worksheet for now.
1215 worksheets : [{cells:cell_array}],
1216 worksheets : [{cells:cell_array}],
1216 metadata : this.metadata
1217 metadata : this.metadata
1217 };
1218 };
1218 return data;
1219 return data;
1219 };
1220 };
1220
1221
1221 Notebook.prototype.save_notebook = function () {
1222 Notebook.prototype.save_notebook = function () {
1222 // We may want to move the name/id/nbformat logic inside toJSON?
1223 // We may want to move the name/id/nbformat logic inside toJSON?
1223 var data = this.toJSON();
1224 var data = this.toJSON();
1224 data.metadata.name = this.notebook_name;
1225 data.metadata.name = this.notebook_name;
1225 data.nbformat = this.nbformat;
1226 data.nbformat = this.nbformat;
1226 // We do the call with settings so we can set cache to false.
1227 // We do the call with settings so we can set cache to false.
1227 var settings = {
1228 var settings = {
1228 processData : false,
1229 processData : false,
1229 cache : false,
1230 cache : false,
1230 type : "PUT",
1231 type : "PUT",
1231 data : JSON.stringify(data),
1232 data : JSON.stringify(data),
1232 headers : {'Content-Type': 'application/json'},
1233 headers : {'Content-Type': 'application/json'},
1233 success : $.proxy(this.save_notebook_success,this),
1234 success : $.proxy(this.save_notebook_success,this),
1234 error : $.proxy(this.save_notebook_error,this)
1235 error : $.proxy(this.save_notebook_error,this)
1235 };
1236 };
1236 $([IPython.events]).trigger('notebook_saving.Notebook');
1237 $([IPython.events]).trigger('notebook_saving.Notebook');
1237 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1238 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1238 $.ajax(url, settings);
1239 $.ajax(url, settings);
1239 };
1240 };
1240
1241
1241
1242
1242 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1243 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1243 this.dirty = false;
1244 this.dirty = false;
1244 $([IPython.events]).trigger('notebook_saved.Notebook');
1245 $([IPython.events]).trigger('notebook_saved.Notebook');
1245 };
1246 };
1246
1247
1247
1248
1248 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1249 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1249 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1250 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1250 };
1251 };
1251
1252
1252
1253
1253 Notebook.prototype.load_notebook = function (notebook_id) {
1254 Notebook.prototype.load_notebook = function (notebook_id) {
1254 var that = this;
1255 var that = this;
1255 this.notebook_id = notebook_id;
1256 this.notebook_id = notebook_id;
1256 // We do the call with settings so we can set cache to false.
1257 // We do the call with settings so we can set cache to false.
1257 var settings = {
1258 var settings = {
1258 processData : false,
1259 processData : false,
1259 cache : false,
1260 cache : false,
1260 type : "GET",
1261 type : "GET",
1261 dataType : "json",
1262 dataType : "json",
1262 success : $.proxy(this.load_notebook_success,this),
1263 success : $.proxy(this.load_notebook_success,this),
1263 error : $.proxy(this.load_notebook_error,this),
1264 error : $.proxy(this.load_notebook_error,this),
1264 };
1265 };
1265 $([IPython.events]).trigger('notebook_loading.Notebook');
1266 $([IPython.events]).trigger('notebook_loading.Notebook');
1266 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1267 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1267 $.ajax(url, settings);
1268 $.ajax(url, settings);
1268 };
1269 };
1269
1270
1270
1271
1271 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1272 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1272 this.fromJSON(data);
1273 this.fromJSON(data);
1273 if (this.ncells() === 0) {
1274 if (this.ncells() === 0) {
1274 this.insert_cell_below('code');
1275 this.insert_cell_below('code');
1275 };
1276 };
1276 this.dirty = false;
1277 this.dirty = false;
1277 if (! this.read_only) {
1278 if (! this.read_only) {
1278 this.start_kernel();
1279 this.start_kernel();
1279 }
1280 }
1280 this.select(0);
1281 this.select(0);
1281 this.scroll_to_top();
1282 this.scroll_to_top();
1282 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1283 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1283 msg = "This notebook has been converted from an older " +
1284 msg = "This notebook has been converted from an older " +
1284 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1285 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1285 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1286 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1286 "newer notebook format will be used and older verions of IPython " +
1287 "newer notebook format will be used and older verions of IPython " +
1287 "may not be able to read it. To keep the older version, close the " +
1288 "may not be able to read it. To keep the older version, close the " +
1288 "notebook without saving it.";
1289 "notebook without saving it.";
1289 var dialog = $('<div/>');
1290 var dialog = $('<div/>');
1290 dialog.html(msg);
1291 dialog.html(msg);
1291 this.element.append(dialog);
1292 this.element.append(dialog);
1292 dialog.dialog({
1293 dialog.dialog({
1293 resizable: false,
1294 resizable: false,
1294 modal: true,
1295 modal: true,
1295 title: "Notebook converted",
1296 title: "Notebook converted",
1296 closeText: "",
1297 closeText: "",
1297 close: function(event, ui) {$(this).dialog('destroy').remove();},
1298 close: function(event, ui) {$(this).dialog('destroy').remove();},
1298 buttons : {
1299 buttons : {
1299 "OK": function () {
1300 "OK": function () {
1300 $(this).dialog('close');
1301 $(this).dialog('close');
1301 }
1302 }
1302 },
1303 },
1303 width: 400
1304 width: 400
1304 });
1305 });
1305 }
1306 }
1306 $([IPython.events]).trigger('notebook_loaded.Notebook');
1307 $([IPython.events]).trigger('notebook_loaded.Notebook');
1307 };
1308 };
1308
1309
1309
1310
1310 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1311 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1311 if (xhr.status === 500) {
1312 if (xhr.status === 500) {
1312 msg = "An error occurred while loading this notebook. Most likely " +
1313 msg = "An error occurred while loading this notebook. Most likely " +
1313 "this notebook is in a newer format than is supported by this " +
1314 "this notebook is in a newer format than is supported by this " +
1314 "version of IPython. This version can load notebook formats " +
1315 "version of IPython. This version can load notebook formats " +
1315 "v"+this.nbformat+" or earlier.";
1316 "v"+this.nbformat+" or earlier.";
1316 var dialog = $('<div/>');
1317 var dialog = $('<div/>');
1317 dialog.html(msg);
1318 dialog.html(msg);
1318 this.element.append(dialog);
1319 this.element.append(dialog);
1319 dialog.dialog({
1320 dialog.dialog({
1320 resizable: false,
1321 resizable: false,
1321 modal: true,
1322 modal: true,
1322 title: "Error loading notebook",
1323 title: "Error loading notebook",
1323 closeText: "",
1324 closeText: "",
1324 close: function(event, ui) {$(this).dialog('destroy').remove();},
1325 close: function(event, ui) {$(this).dialog('destroy').remove();},
1325 buttons : {
1326 buttons : {
1326 "OK": function () {
1327 "OK": function () {
1327 $(this).dialog('close');
1328 $(this).dialog('close');
1328 }
1329 }
1329 },
1330 },
1330 width: 400
1331 width: 400
1331 });
1332 });
1332 }
1333 }
1333 }
1334 }
1334
1335
1335 IPython.Notebook = Notebook;
1336 IPython.Notebook = Notebook;
1336
1337
1337
1338
1338 return IPython;
1339 return IPython;
1339
1340
1340 }(IPython));
1341 }(IPython));
1341
1342
General Comments 0
You need to be logged in to leave comments. Login now