##// END OF EJS Templates
don't preserve fixConsole output in json
MinRK -
Show More
@@ -1,839 +1,843 b''
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 = ' ';
18 this.input_prompt_number = ' ';
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 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
86 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
87 // Always ignore shift-enter in CodeMirror as we handle it.
87 // Always ignore shift-enter in CodeMirror as we handle it.
88 return true;
88 return true;
89 }else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
89 }else if (event.which === 40 && event.type === 'keypress' && tooltip_wait_time >= 0) {
90 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
90 // triger aon keypress (!) otherwise inconsistent event.which depending on plateform
91 // browser and keyboard layout !
91 // browser and keyboard layout !
92 // Pressing '(' , request tooltip, don't forget to reappend it
92 // Pressing '(' , request tooltip, don't forget to reappend it
93 var cursor = editor.getCursor();
93 var cursor = editor.getCursor();
94 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
94 var pre_cursor = editor.getRange({line:cursor.line,ch:0},cursor).trim()+'(';
95 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
95 that.request_tooltip_after_time(pre_cursor,tooltip_wait_time);
96 } else if (event.keyCode === 9 && event.type == 'keydown') {
96 } else if (event.keyCode === 9 && event.type == 'keydown') {
97 // Tab completion.
97 // Tab completion.
98 var cur = editor.getCursor();
98 var cur = editor.getCursor();
99 //Do not trim here because of tooltip
99 //Do not trim here because of tooltip
100 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
100 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
101 if (pre_cursor.trim() === "") {
101 if (pre_cursor.trim() === "") {
102 // Don't autocomplete if the part of the line before the cursor
102 // Don't autocomplete if the part of the line before the cursor
103 // is empty. In this case, let CodeMirror handle indentation.
103 // is empty. In this case, let CodeMirror handle indentation.
104 return false;
104 return false;
105 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
105 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && tooltip_on_tab ) {
106 that.request_tooltip_after_time(pre_cursor,0);
106 that.request_tooltip_after_time(pre_cursor,0);
107 } else {
107 } else {
108 pre_cursor.trim();
108 pre_cursor.trim();
109 // Autocomplete the current line.
109 // Autocomplete the current line.
110 event.stop();
110 event.stop();
111 var line = editor.getLine(cur.line);
111 var line = editor.getLine(cur.line);
112 this.is_completing = true;
112 this.is_completing = true;
113 this.completion_cursor = cur;
113 this.completion_cursor = cur;
114 IPython.notebook.complete_cell(this, line, cur.ch);
114 IPython.notebook.complete_cell(this, line, cur.ch);
115 return true;
115 return true;
116 }
116 }
117 } else if (event.keyCode === 8 && event.type == 'keydown') {
117 } else if (event.keyCode === 8 && event.type == 'keydown') {
118 // If backspace and the line ends with 4 spaces, remove them.
118 // If backspace and the line ends with 4 spaces, remove them.
119 var cur = editor.getCursor();
119 var cur = editor.getCursor();
120 var line = editor.getLine(cur.line);
120 var line = editor.getLine(cur.line);
121 var ending = line.slice(-4);
121 var ending = line.slice(-4);
122 if (ending === ' ') {
122 if (ending === ' ') {
123 editor.replaceRange('',
123 editor.replaceRange('',
124 {line: cur.line, ch: cur.ch-4},
124 {line: cur.line, ch: cur.ch-4},
125 {line: cur.line, ch: cur.ch}
125 {line: cur.line, ch: cur.ch}
126 );
126 );
127 event.stop();
127 event.stop();
128 return true;
128 return true;
129 } else {
129 } else {
130 return false;
130 return false;
131 }
131 }
132 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
132 } else if (event.keyCode === 76 && event.ctrlKey && event.shiftKey
133 && event.type == 'keydown') {
133 && event.type == 'keydown') {
134 // toggle line numbers with Ctrl-Shift-L
134 // toggle line numbers with Ctrl-Shift-L
135 this.toggle_line_numbers();
135 this.toggle_line_numbers();
136 }
136 }
137 else {
137 else {
138 // keypress/keyup also trigger on TAB press, and we don't want to
138 // keypress/keyup also trigger on TAB press, and we don't want to
139 // use those to disable tab completion.
139 // use those to disable tab completion.
140 if (this.is_completing && event.keyCode !== 9) {
140 if (this.is_completing && event.keyCode !== 9) {
141 var ed_cur = editor.getCursor();
141 var ed_cur = editor.getCursor();
142 var cc_cur = this.completion_cursor;
142 var cc_cur = this.completion_cursor;
143 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
143 if (ed_cur.line !== cc_cur.line || ed_cur.ch !== cc_cur.ch) {
144 this.is_completing = false;
144 this.is_completing = false;
145 this.completion_cursor = null;
145 this.completion_cursor = null;
146 }
146 }
147 }
147 }
148 return false;
148 return false;
149 };
149 };
150 return false;
150 return false;
151 };
151 };
152
152
153 CodeCell.prototype.remove_and_cancel_tooltip = function() {
153 CodeCell.prototype.remove_and_cancel_tooltip = function() {
154 // note that we don't handle closing directly inside the calltip
154 // note that we don't handle closing directly inside the calltip
155 // as in the completer, because it is not focusable, so won't
155 // as in the completer, because it is not focusable, so won't
156 // get the event.
156 // get the event.
157 if (this.tooltip_timeout != null){
157 if (this.tooltip_timeout != null){
158 clearTimeout(this.tooltip_timeout);
158 clearTimeout(this.tooltip_timeout);
159 $('#tooltip').remove();
159 $('#tooltip').remove();
160 this.tooltip_timeout = null;
160 this.tooltip_timeout = null;
161 }
161 }
162 }
162 }
163
163
164 CodeCell.prototype.finish_tooltip = function (reply) {
164 CodeCell.prototype.finish_tooltip = function (reply) {
165 // Extract call tip data; the priority is call, init, main.
165 // Extract call tip data; the priority is call, init, main.
166 defstring = reply.call_def;
166 defstring = reply.call_def;
167 if (defstring == null) { defstring = reply.init_definition; }
167 if (defstring == null) { defstring = reply.init_definition; }
168 if (defstring == null) { defstring = reply.definition; }
168 if (defstring == null) { defstring = reply.definition; }
169
169
170 docstring = reply.call_docstring;
170 docstring = reply.call_docstring;
171 if (docstring == null) { docstring = reply.init_docstring; }
171 if (docstring == null) { docstring = reply.init_docstring; }
172 if (docstring == null) { docstring = reply.docstring; }
172 if (docstring == null) { docstring = reply.docstring; }
173 if (docstring == null) { docstring = "<empty docstring>"; }
173 if (docstring == null) { docstring = "<empty docstring>"; }
174
174
175 name=reply.name;
175 name=reply.name;
176
176
177 var that = this;
177 var that = this;
178 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
178 var tooltip = $('<div/>').attr('id', 'tooltip').addClass('tooltip');
179 // remove to have the tooltip not Limited in X and Y
179 // remove to have the tooltip not Limited in X and Y
180 tooltip.addClass('smalltooltip');
180 tooltip.addClass('smalltooltip');
181 var pre=$('<pre/>').html(utils.fixConsole(docstring));
181 var pre=$('<pre/>').html(utils.fixConsole(docstring));
182 var expandlink=$('<a/>').attr('href',"#");
182 var expandlink=$('<a/>').attr('href',"#");
183 expandlink.addClass("ui-corner-all"); //rounded corner
183 expandlink.addClass("ui-corner-all"); //rounded corner
184 expandlink.attr('role',"button");
184 expandlink.attr('role',"button");
185 //expandlink.addClass('ui-button');
185 //expandlink.addClass('ui-button');
186 //expandlink.addClass('ui-state-default');
186 //expandlink.addClass('ui-state-default');
187 var expandspan=$('<span/>').text('Expand');
187 var expandspan=$('<span/>').text('Expand');
188 expandspan.addClass('ui-icon');
188 expandspan.addClass('ui-icon');
189 expandspan.addClass('ui-icon-plus');
189 expandspan.addClass('ui-icon-plus');
190 expandlink.append(expandspan);
190 expandlink.append(expandspan);
191 expandlink.attr('id','expanbutton');
191 expandlink.attr('id','expanbutton');
192 expandlink.click(function(){
192 expandlink.click(function(){
193 tooltip.removeClass('smalltooltip');
193 tooltip.removeClass('smalltooltip');
194 tooltip.addClass('bigtooltip');
194 tooltip.addClass('bigtooltip');
195 $('#expanbutton').remove();
195 $('#expanbutton').remove();
196 setTimeout(function(){that.code_mirror.focus();}, 50);
196 setTimeout(function(){that.code_mirror.focus();}, 50);
197 });
197 });
198 var morelink=$('<a/>').attr('href',"#");
198 var morelink=$('<a/>').attr('href',"#");
199 morelink.attr('role',"button");
199 morelink.attr('role',"button");
200 morelink.addClass('ui-button');
200 morelink.addClass('ui-button');
201 //morelink.addClass("ui-corner-all"); //rounded corner
201 //morelink.addClass("ui-corner-all"); //rounded corner
202 //morelink.addClass('ui-state-default');
202 //morelink.addClass('ui-state-default');
203 var morespan=$('<span/>').text('Open in Pager');
203 var morespan=$('<span/>').text('Open in Pager');
204 morespan.addClass('ui-icon');
204 morespan.addClass('ui-icon');
205 morespan.addClass('ui-icon-arrowstop-l-n');
205 morespan.addClass('ui-icon-arrowstop-l-n');
206 morelink.append(morespan);
206 morelink.append(morespan);
207 morelink.click(function(){
207 morelink.click(function(){
208 var msg_id = IPython.notebook.kernel.execute(name+"?");
208 var msg_id = IPython.notebook.kernel.execute(name+"?");
209 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
209 IPython.notebook.msg_cell_map[msg_id] = IPython.notebook.selected_cell().cell_id;
210 that.remove_and_cancel_tooltip();
210 that.remove_and_cancel_tooltip();
211 setTimeout(function(){that.code_mirror.focus();}, 50);
211 setTimeout(function(){that.code_mirror.focus();}, 50);
212 });
212 });
213
213
214 var closelink=$('<a/>').attr('href',"#");
214 var closelink=$('<a/>').attr('href',"#");
215 closelink.attr('role',"button");
215 closelink.attr('role',"button");
216 closelink.addClass('ui-button');
216 closelink.addClass('ui-button');
217 //closelink.addClass("ui-corner-all"); //rounded corner
217 //closelink.addClass("ui-corner-all"); //rounded corner
218 //closelink.adClass('ui-state-default'); // grey background and blue cross
218 //closelink.adClass('ui-state-default'); // grey background and blue cross
219 var closespan=$('<span/>').text('Close');
219 var closespan=$('<span/>').text('Close');
220 closespan.addClass('ui-icon');
220 closespan.addClass('ui-icon');
221 closespan.addClass('ui-icon-close');
221 closespan.addClass('ui-icon-close');
222 closelink.append(closespan);
222 closelink.append(closespan);
223 closelink.click(function(){
223 closelink.click(function(){
224 that.remove_and_cancel_tooltip();
224 that.remove_and_cancel_tooltip();
225 setTimeout(function(){that.code_mirror.focus();}, 50);
225 setTimeout(function(){that.code_mirror.focus();}, 50);
226 });
226 });
227 //construct the tooltip
227 //construct the tooltip
228 tooltip.append(closelink);
228 tooltip.append(closelink);
229 tooltip.append(expandlink);
229 tooltip.append(expandlink);
230 tooltip.append(morelink);
230 tooltip.append(morelink);
231 if(defstring){
231 if(defstring){
232 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
232 defstring_html = $('<pre/>').html(utils.fixConsole(defstring));
233 tooltip.append(defstring_html);
233 tooltip.append(defstring_html);
234 }
234 }
235 tooltip.append(pre);
235 tooltip.append(pre);
236 var pos = this.code_mirror.cursorCoords();
236 var pos = this.code_mirror.cursorCoords();
237 tooltip.css('left',pos.x+'px');
237 tooltip.css('left',pos.x+'px');
238 tooltip.css('top',pos.yBot+'px');
238 tooltip.css('top',pos.yBot+'px');
239 $('body').append(tooltip);
239 $('body').append(tooltip);
240
240
241 // issues with cross-closing if multiple tooltip in less than 5sec
241 // issues with cross-closing if multiple tooltip in less than 5sec
242 // keep it comented for now
242 // keep it comented for now
243 // setTimeout(that.remove_and_cancel_tooltip, 5000);
243 // setTimeout(that.remove_and_cancel_tooltip, 5000);
244 };
244 };
245
245
246 // As you type completer
246 // As you type completer
247 CodeCell.prototype.finish_completing = function (matched_text, matches) {
247 CodeCell.prototype.finish_completing = function (matched_text, matches) {
248 //return if not completing or nothing to complete
248 //return if not completing or nothing to complete
249 if (!this.is_completing || matches.length === 0) {return;}
249 if (!this.is_completing || matches.length === 0) {return;}
250
250
251 // for later readability
251 // for later readability
252 var key = { tab:9,
252 var key = { tab:9,
253 esc:27,
253 esc:27,
254 backspace:8,
254 backspace:8,
255 space:32,
255 space:32,
256 shift:16,
256 shift:16,
257 enter:13,
257 enter:13,
258 // _ is 95
258 // _ is 95
259 isCompSymbol : function (code)
259 isCompSymbol : function (code)
260 {
260 {
261 return (code > 64 && code <= 90)
261 return (code > 64 && code <= 90)
262 || (code >= 97 && code <= 122)
262 || (code >= 97 && code <= 122)
263 || (code == 95)
263 || (code == 95)
264 },
264 },
265 dismissAndAppend : function (code)
265 dismissAndAppend : function (code)
266 {
266 {
267 chararr = '()[]+-/\\. ,=*'.split("");
267 chararr = '()[]+-/\\. ,=*'.split("");
268 codearr = chararr.map(function(x){return x.charCodeAt(0)});
268 codearr = chararr.map(function(x){return x.charCodeAt(0)});
269 return jQuery.inArray(code, codearr) != -1;
269 return jQuery.inArray(code, codearr) != -1;
270 }
270 }
271
271
272 }
272 }
273
273
274 // smart completion, sort kwarg ending with '='
274 // smart completion, sort kwarg ending with '='
275 var newm = new Array();
275 var newm = new Array();
276 if(this.notebook.smart_completer)
276 if(this.notebook.smart_completer)
277 {
277 {
278 kwargs = new Array();
278 kwargs = new Array();
279 other = new Array();
279 other = new Array();
280 for(var i = 0 ; i<matches.length ; ++i){
280 for(var i = 0 ; i<matches.length ; ++i){
281 if(matches[i].substr(-1) === '='){
281 if(matches[i].substr(-1) === '='){
282 kwargs.push(matches[i]);
282 kwargs.push(matches[i]);
283 }else{other.push(matches[i]);}
283 }else{other.push(matches[i]);}
284 }
284 }
285 newm = kwargs.concat(other);
285 newm = kwargs.concat(other);
286 matches = newm;
286 matches = newm;
287 }
287 }
288 // end sort kwargs
288 // end sort kwargs
289
289
290 // give common prefix of a array of string
290 // give common prefix of a array of string
291 function sharedStart(A){
291 function sharedStart(A){
292 if(A.length == 1){return A[0]}
292 if(A.length == 1){return A[0]}
293 if(A.length > 1 ){
293 if(A.length > 1 ){
294 var tem1, tem2, s, A = A.slice(0).sort();
294 var tem1, tem2, s, A = A.slice(0).sort();
295 tem1 = A[0];
295 tem1 = A[0];
296 s = tem1.length;
296 s = tem1.length;
297 tem2 = A.pop();
297 tem2 = A.pop();
298 while(s && tem2.indexOf(tem1) == -1){
298 while(s && tem2.indexOf(tem1) == -1){
299 tem1 = tem1.substring(0, --s);
299 tem1 = tem1.substring(0, --s);
300 }
300 }
301 return tem1;
301 return tem1;
302 }
302 }
303 return "";
303 return "";
304 }
304 }
305
305
306
306
307 //try to check if the user is typing tab at least twice after a word
307 //try to check if the user is typing tab at least twice after a word
308 // and completion is "done"
308 // and completion is "done"
309 fallback_on_tooltip_after = 2
309 fallback_on_tooltip_after = 2
310 if(matches.length == 1 && matched_text === matches[0])
310 if(matches.length == 1 && matched_text === matches[0])
311 {
311 {
312 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
312 if(this.npressed >fallback_on_tooltip_after && this.prevmatch==matched_text)
313 {
313 {
314 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
314 console.log('Ok, you really want to complete after pressing tab '+this.npressed+' times !');
315 console.log('You should understand that there is no (more) completion for that !');
315 console.log('You should understand that there is no (more) completion for that !');
316 console.log("I'll show you the tooltip, will you stop bothering me ?");
316 console.log("I'll show you the tooltip, will you stop bothering me ?");
317 this.request_tooltip_after_time(matched_text+'(',0);
317 this.request_tooltip_after_time(matched_text+'(',0);
318 return;
318 return;
319 }
319 }
320 this.prevmatch = matched_text
320 this.prevmatch = matched_text
321 this.npressed = this.npressed+1;
321 this.npressed = this.npressed+1;
322 }
322 }
323 else
323 else
324 {
324 {
325 this.prevmatch = "";
325 this.prevmatch = "";
326 this.npressed = 0;
326 this.npressed = 0;
327 }
327 }
328 // end fallback on tooltip
328 // end fallback on tooltip
329 //==================================
329 //==================================
330 // Real completion logic start here
330 // Real completion logic start here
331 var that = this;
331 var that = this;
332 var cur = this.completion_cursor;
332 var cur = this.completion_cursor;
333 var done = false;
333 var done = false;
334
334
335 // call to dismmiss the completer
335 // call to dismmiss the completer
336 var close = function () {
336 var close = function () {
337 if (done) return;
337 if (done) return;
338 done = true;
338 done = true;
339 if (complete != undefined)
339 if (complete != undefined)
340 {complete.remove();}
340 {complete.remove();}
341 that.is_completing = false;
341 that.is_completing = false;
342 that.completion_cursor = null;
342 that.completion_cursor = null;
343 };
343 };
344
344
345 // update codemirror with the typed text
345 // update codemirror with the typed text
346 prev = matched_text
346 prev = matched_text
347 var update = function (inserted_text, event) {
347 var update = function (inserted_text, event) {
348 that.code_mirror.replaceRange(
348 that.code_mirror.replaceRange(
349 inserted_text,
349 inserted_text,
350 {line: cur.line, ch: (cur.ch-matched_text.length)},
350 {line: cur.line, ch: (cur.ch-matched_text.length)},
351 {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
351 {line: cur.line, ch: (cur.ch+prev.length-matched_text.length)}
352 );
352 );
353 prev = inserted_text
353 prev = inserted_text
354 if(event != null){
354 if(event != null){
355 event.stopPropagation();
355 event.stopPropagation();
356 event.preventDefault();
356 event.preventDefault();
357 }
357 }
358 };
358 };
359 // insert the given text and exit the completer
359 // insert the given text and exit the completer
360 var insert = function (selected_text, event) {
360 var insert = function (selected_text, event) {
361 update(selected_text)
361 update(selected_text)
362 close();
362 close();
363 setTimeout(function(){that.code_mirror.focus();}, 50);
363 setTimeout(function(){that.code_mirror.focus();}, 50);
364 };
364 };
365
365
366 // insert the curent highlited selection and exit
366 // insert the curent highlited selection and exit
367 var pick = function () {
367 var pick = function () {
368 insert(select.val()[0],null);
368 insert(select.val()[0],null);
369 };
369 };
370
370
371
371
372 // Define function to clear the completer, refill it with the new
372 // Define function to clear the completer, refill it with the new
373 // matches, update the pseuso typing field. autopick insert match if
373 // matches, update the pseuso typing field. autopick insert match if
374 // only one left, in no matches (anymore) dismiss itself by pasting
374 // only one left, in no matches (anymore) dismiss itself by pasting
375 // what the user have typed until then
375 // what the user have typed until then
376 var complete_with = function(matches,typed_text,autopick,event)
376 var complete_with = function(matches,typed_text,autopick,event)
377 {
377 {
378 // If autopick an only one match, past.
378 // If autopick an only one match, past.
379 // Used to 'pick' when pressing tab
379 // Used to 'pick' when pressing tab
380 if (matches.length < 1) {
380 if (matches.length < 1) {
381 insert(typed_text,event);
381 insert(typed_text,event);
382 if(event != null){
382 if(event != null){
383 event.stopPropagation();
383 event.stopPropagation();
384 event.preventDefault();
384 event.preventDefault();
385 }
385 }
386 } else if (autopick && matches.length == 1) {
386 } else if (autopick && matches.length == 1) {
387 insert(matches[0],event);
387 insert(matches[0],event);
388 if(event != null){
388 if(event != null){
389 event.stopPropagation();
389 event.stopPropagation();
390 event.preventDefault();
390 event.preventDefault();
391 }
391 }
392 }
392 }
393 //clear the previous completion if any
393 //clear the previous completion if any
394 update(typed_text,event);
394 update(typed_text,event);
395 complete.children().children().remove();
395 complete.children().children().remove();
396 $('#asyoutype').html("<b>"+matched_text+"</b>"+typed_text.substr(matched_text.length));
396 $('#asyoutype').html("<b>"+matched_text+"</b>"+typed_text.substr(matched_text.length));
397 select = $('#asyoutypeselect');
397 select = $('#asyoutypeselect');
398 for (var i = 0; i<matches.length; ++i) {
398 for (var i = 0; i<matches.length; ++i) {
399 select.append($('<option/>').html(matches[i]));
399 select.append($('<option/>').html(matches[i]));
400 }
400 }
401 select.children().first().attr('selected','true');
401 select.children().first().attr('selected','true');
402 }
402 }
403
403
404 // create html for completer
404 // create html for completer
405 var complete = $('<div/>').addClass('completions');
405 var complete = $('<div/>').addClass('completions');
406 complete.attr('id','complete');
406 complete.attr('id','complete');
407 complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
407 complete.append($('<p/>').attr('id', 'asyoutype').html('<b>fixed part</b>user part'));//pseudo input field
408
408
409 var select = $('<select/>').attr('multiple','true');
409 var select = $('<select/>').attr('multiple','true');
410 select.attr('id', 'asyoutypeselect')
410 select.attr('id', 'asyoutypeselect')
411 select.attr('size',Math.min(10,matches.length));
411 select.attr('size',Math.min(10,matches.length));
412 var pos = this.code_mirror.cursorCoords();
412 var pos = this.code_mirror.cursorCoords();
413
413
414 // TODO: I propose to remove enough horizontal pixel
414 // TODO: I propose to remove enough horizontal pixel
415 // to align the text later
415 // to align the text later
416 complete.css('left',pos.x+'px');
416 complete.css('left',pos.x+'px');
417 complete.css('top',pos.yBot+'px');
417 complete.css('top',pos.yBot+'px');
418 complete.append(select);
418 complete.append(select);
419
419
420 $('body').append(complete);
420 $('body').append(complete);
421
421
422 // So a first actual completion. see if all the completion start wit
422 // So a first actual completion. see if all the completion start wit
423 // the same letter and complete if necessary
423 // the same letter and complete if necessary
424 fastForward = sharedStart(matches)
424 fastForward = sharedStart(matches)
425 typed_characters = fastForward.substr(matched_text.length);
425 typed_characters = fastForward.substr(matched_text.length);
426 complete_with(matches,matched_text+typed_characters,true,null);
426 complete_with(matches,matched_text+typed_characters,true,null);
427 filterd = matches;
427 filterd = matches;
428 // Give focus to select, and make it filter the match as the user type
428 // Give focus to select, and make it filter the match as the user type
429 // by filtering the previous matches. Called by .keypress and .keydown
429 // by filtering the previous matches. Called by .keypress and .keydown
430 var downandpress = function (event,press_or_down) {
430 var downandpress = function (event,press_or_down) {
431 var code = event.which;
431 var code = event.which;
432 var autopick = false; // auto 'pick' if only one match
432 var autopick = false; // auto 'pick' if only one match
433 if (press_or_down === 0){
433 if (press_or_down === 0){
434 press = true; down = false; //Are we called from keypress or keydown
434 press = true; down = false; //Are we called from keypress or keydown
435 } else if (press_or_down == 1){
435 } else if (press_or_down == 1){
436 press = false; down = true;
436 press = false; down = true;
437 }
437 }
438 if (code === key.shift) {
438 if (code === key.shift) {
439 // nothing on Shift
439 // nothing on Shift
440 return;
440 return;
441 }
441 }
442 if (key.dismissAndAppend(code) && press) {
442 if (key.dismissAndAppend(code) && press) {
443 var newchar = String.fromCharCode(code);
443 var newchar = String.fromCharCode(code);
444 typed_characters = typed_characters+newchar;
444 typed_characters = typed_characters+newchar;
445 insert(matched_text+typed_characters,event);
445 insert(matched_text+typed_characters,event);
446 return
446 return
447 }
447 }
448 if (code === key.enter) {
448 if (code === key.enter) {
449 // Pressing ENTER will cause a pick
449 // Pressing ENTER will cause a pick
450 event.stopPropagation();
450 event.stopPropagation();
451 event.preventDefault();
451 event.preventDefault();
452 pick();
452 pick();
453 } else if (code === 38 || code === 40) {
453 } else if (code === 38 || code === 40) {
454 // We don't want the document keydown handler to handle UP/DOWN,
454 // We don't want the document keydown handler to handle UP/DOWN,
455 // but we want the default action.
455 // but we want the default action.
456 event.stopPropagation();
456 event.stopPropagation();
457 } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
457 } else if ( (code == key.backspace)||(code == key.tab && down) || press || key.isCompSymbol(code)){
458 if( key.isCompSymbol(code) && press)
458 if( key.isCompSymbol(code) && press)
459 {
459 {
460 var newchar = String.fromCharCode(code);
460 var newchar = String.fromCharCode(code);
461 typed_characters = typed_characters+newchar;
461 typed_characters = typed_characters+newchar;
462 } else if (code == key.tab) {
462 } else if (code == key.tab) {
463 fastForward = sharedStart(filterd)
463 fastForward = sharedStart(filterd)
464 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
464 ffsub = fastForward.substr(matched_text.length+typed_characters.length);
465 typed_characters = typed_characters+ffsub;
465 typed_characters = typed_characters+ffsub;
466 autopick = true;
466 autopick = true;
467 } else if (code == key.backspace && down) {
467 } else if (code == key.backspace && down) {
468 // cancel if user have erase everything, otherwise decrease
468 // cancel if user have erase everything, otherwise decrease
469 // what we filter with
469 // what we filter with
470 event.preventDefault();
470 event.preventDefault();
471 if (typed_characters.length <= 0)
471 if (typed_characters.length <= 0)
472 {
472 {
473 insert(matched_text,event)
473 insert(matched_text,event)
474 return
474 return
475 }
475 }
476 typed_characters = typed_characters.substr(0,typed_characters.length-1);
476 typed_characters = typed_characters.substr(0,typed_characters.length-1);
477 } else if (press && code != key.backspace && code != key.tab && code != 0){
477 } else if (press && code != key.backspace && code != key.tab && code != 0){
478 insert(matched_text+typed_characters,event);
478 insert(matched_text+typed_characters,event);
479 return
479 return
480 } else {
480 } else {
481 return
481 return
482 }
482 }
483 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
483 re = new RegExp("^"+"\%?"+matched_text+typed_characters,"");
484 filterd = matches.filter(function(x){return re.test(x)});
484 filterd = matches.filter(function(x){return re.test(x)});
485 complete_with(filterd,matched_text+typed_characters,autopick,event);
485 complete_with(filterd,matched_text+typed_characters,autopick,event);
486 } else if( code == key.esc) {
486 } else if( code == key.esc) {
487 // dismiss the completer and go back to before invoking it
487 // dismiss the completer and go back to before invoking it
488 insert(matched_text,event);
488 insert(matched_text,event);
489 } else if( press ){ // abort only on .keypress or esc
489 } else if( press ){ // abort only on .keypress or esc
490 // abort with what the user have pressed until now
490 // abort with what the user have pressed until now
491 console.log('aborting with keycode : '+code+' is down :'+down);
491 console.log('aborting with keycode : '+code+' is down :'+down);
492 }
492 }
493 }
493 }
494 select.keydown(function (event) {
494 select.keydown(function (event) {
495 downandpress(event,1)
495 downandpress(event,1)
496 });
496 });
497 select.keypress(function (event) {
497 select.keypress(function (event) {
498 downandpress(event,0)
498 downandpress(event,0)
499 });
499 });
500 // Double click also causes a pick.
500 // Double click also causes a pick.
501 // and bind the last actions.
501 // and bind the last actions.
502 select.dblclick(pick);
502 select.dblclick(pick);
503 select.blur(close);
503 select.blur(close);
504 select.focus();
504 select.focus();
505 };
505 };
506
506
507 CodeCell.prototype.toggle_line_numbers = function () {
507 CodeCell.prototype.toggle_line_numbers = function () {
508 if (this.code_mirror.getOption('lineNumbers') == false) {
508 if (this.code_mirror.getOption('lineNumbers') == false) {
509 this.code_mirror.setOption('lineNumbers', true);
509 this.code_mirror.setOption('lineNumbers', true);
510 } else {
510 } else {
511 this.code_mirror.setOption('lineNumbers', false);
511 this.code_mirror.setOption('lineNumbers', false);
512 }
512 }
513 this.code_mirror.refresh();
513 this.code_mirror.refresh();
514 };
514 };
515
515
516 CodeCell.prototype.select = function () {
516 CodeCell.prototype.select = function () {
517 IPython.Cell.prototype.select.apply(this);
517 IPython.Cell.prototype.select.apply(this);
518 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
518 // Todo: this dance is needed because as of CodeMirror 2.12, focus is
519 // not causing the cursor to blink if the editor is empty initially.
519 // not causing the cursor to blink if the editor is empty initially.
520 // While this seems to fix the issue, this should be fixed
520 // While this seems to fix the issue, this should be fixed
521 // in CodeMirror proper.
521 // in CodeMirror proper.
522 var s = this.code_mirror.getValue();
522 var s = this.code_mirror.getValue();
523 this.code_mirror.focus();
523 this.code_mirror.focus();
524 if (s === '') this.code_mirror.setValue('');
524 if (s === '') this.code_mirror.setValue('');
525 };
525 };
526
526
527
527
528 CodeCell.prototype.select_all = function () {
528 CodeCell.prototype.select_all = function () {
529 var start = {line: 0, ch: 0};
529 var start = {line: 0, ch: 0};
530 var nlines = this.code_mirror.lineCount();
530 var nlines = this.code_mirror.lineCount();
531 var last_line = this.code_mirror.getLine(nlines-1);
531 var last_line = this.code_mirror.getLine(nlines-1);
532 var end = {line: nlines-1, ch: last_line.length};
532 var end = {line: nlines-1, ch: last_line.length};
533 this.code_mirror.setSelection(start, end);
533 this.code_mirror.setSelection(start, end);
534 };
534 };
535
535
536
536
537 CodeCell.prototype.append_output = function (json) {
537 CodeCell.prototype.append_output = function (json) {
538 this.expand();
538 this.expand();
539 if (json.output_type === 'pyout') {
539 if (json.output_type === 'pyout') {
540 this.append_pyout(json);
540 this.append_pyout(json);
541 } else if (json.output_type === 'pyerr') {
541 } else if (json.output_type === 'pyerr') {
542 this.append_pyerr(json);
542 this.append_pyerr(json);
543 } else if (json.output_type === 'display_data') {
543 } else if (json.output_type === 'display_data') {
544 this.append_display_data(json);
544 this.append_display_data(json);
545 } else if (json.output_type === 'stream') {
545 } else if (json.output_type === 'stream') {
546 this.append_stream(json);
546 this.append_stream(json);
547 };
547 };
548 this.outputs.push(json);
548 this.outputs.push(json);
549 };
549 };
550
550
551
551
552 CodeCell.prototype.create_output_area = function () {
552 CodeCell.prototype.create_output_area = function () {
553 var oa = $("<div/>").addClass("hbox output_area");
553 var oa = $("<div/>").addClass("hbox output_area");
554 oa.append($('<div/>').addClass('prompt'));
554 oa.append($('<div/>').addClass('prompt'));
555 return oa;
555 return oa;
556 };
556 };
557
557
558
558
559 CodeCell.prototype.append_pyout = function (json) {
559 CodeCell.prototype.append_pyout = function (json) {
560 n = json.prompt_number || ' ';
560 n = json.prompt_number || ' ';
561 var toinsert = this.create_output_area();
561 var toinsert = this.create_output_area();
562 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
562 toinsert.find('div.prompt').addClass('output_prompt').html('Out[' + n + ']:');
563 this.append_mime_type(json, toinsert);
563 this.append_mime_type(json, toinsert);
564 this.element.find('div.output').append(toinsert);
564 this.element.find('div.output').append(toinsert);
565 // If we just output latex, typeset it.
565 // If we just output latex, typeset it.
566 if ((json.latex !== undefined) || (json.html !== undefined)) {
566 if ((json.latex !== undefined) || (json.html !== undefined)) {
567 this.typeset();
567 this.typeset();
568 };
568 };
569 };
569 };
570
570
571
571
572 CodeCell.prototype.append_pyerr = function (json) {
572 CodeCell.prototype.append_pyerr = function (json) {
573 var tb = json.traceback;
573 var tb = json.traceback;
574 if (tb !== undefined && tb.length > 0) {
574 if (tb !== undefined && tb.length > 0) {
575 var s = '';
575 var s = '';
576 var len = tb.length;
576 var len = tb.length;
577 for (var i=0; i<len; i++) {
577 for (var i=0; i<len; i++) {
578 s = s + tb[i] + '\n';
578 s = s + tb[i] + '\n';
579 }
579 }
580 s = s + '\n';
580 s = s + '\n';
581 var toinsert = this.create_output_area();
581 var toinsert = this.create_output_area();
582 this.append_text(s, toinsert);
582 this.append_text(s, toinsert);
583 this.element.find('div.output').append(toinsert);
583 this.element.find('div.output').append(toinsert);
584 };
584 };
585 };
585 };
586
586
587
587
588 CodeCell.prototype.append_stream = function (json) {
588 CodeCell.prototype.append_stream = function (json) {
589 // temporary fix: if stream undefined (json file written prior to this patch),
589 // temporary fix: if stream undefined (json file written prior to this patch),
590 // default to most likely stdout:
590 // default to most likely stdout:
591 if (json.stream == undefined){
591 if (json.stream == undefined){
592 json.stream = 'stdout';
592 json.stream = 'stdout';
593 }
593 }
594 var subclass = "output_"+json.stream;
594 var subclass = "output_"+json.stream;
595 if (this.outputs.length > 0){
595 if (this.outputs.length > 0){
596 // have at least one output to consider
596 // have at least one output to consider
597 var last = this.outputs[this.outputs.length-1];
597 var last = this.outputs[this.outputs.length-1];
598 if (last.output_type == 'stream' && json.stream == last.stream){
598 if (last.output_type == 'stream' && json.stream == last.stream){
599 // latest output was in the same stream,
599 // latest output was in the same stream,
600 // so append directly into its pre tag
600 // so append directly into its pre tag
601 this.element.find('div.'+subclass).last().find('pre').append(json.text);
601 // escape ANSI & HTML specials:
602 var text = utils.fixConsole(json.text);
603 this.element.find('div.'+subclass).last().find('pre').append(text);
602 return;
604 return;
603 }
605 }
604 }
606 }
605
607
606 // If we got here, attach a new div
608 // If we got here, attach a new div
607 var toinsert = this.create_output_area();
609 var toinsert = this.create_output_area();
608 this.append_text(json.text, toinsert, "output_stream "+subclass);
610 this.append_text(json.text, toinsert, "output_stream "+subclass);
609 this.element.find('div.output').append(toinsert);
611 this.element.find('div.output').append(toinsert);
610 };
612 };
611
613
612
614
613 CodeCell.prototype.append_display_data = function (json) {
615 CodeCell.prototype.append_display_data = function (json) {
614 var toinsert = this.create_output_area();
616 var toinsert = this.create_output_area();
615 this.append_mime_type(json, toinsert);
617 this.append_mime_type(json, toinsert);
616 this.element.find('div.output').append(toinsert);
618 this.element.find('div.output').append(toinsert);
617 // If we just output latex, typeset it.
619 // If we just output latex, typeset it.
618 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
620 if ( (json.latex !== undefined) || (json.html !== undefined) ) {
619 this.typeset();
621 this.typeset();
620 };
622 };
621 };
623 };
622
624
623
625
624 CodeCell.prototype.append_mime_type = function (json, element) {
626 CodeCell.prototype.append_mime_type = function (json, element) {
625 if (json.html !== undefined) {
627 if (json.html !== undefined) {
626 this.append_html(json.html, element);
628 this.append_html(json.html, element);
627 } else if (json.latex !== undefined) {
629 } else if (json.latex !== undefined) {
628 this.append_latex(json.latex, element);
630 this.append_latex(json.latex, element);
629 } else if (json.svg !== undefined) {
631 } else if (json.svg !== undefined) {
630 this.append_svg(json.svg, element);
632 this.append_svg(json.svg, element);
631 } else if (json.png !== undefined) {
633 } else if (json.png !== undefined) {
632 this.append_png(json.png, element);
634 this.append_png(json.png, element);
633 } else if (json.jpeg !== undefined) {
635 } else if (json.jpeg !== undefined) {
634 this.append_jpeg(json.jpeg, element);
636 this.append_jpeg(json.jpeg, element);
635 } else if (json.text !== undefined) {
637 } else if (json.text !== undefined) {
636 this.append_text(json.text, element);
638 this.append_text(json.text, element);
637 };
639 };
638 };
640 };
639
641
640
642
641 CodeCell.prototype.append_html = function (html, element) {
643 CodeCell.prototype.append_html = function (html, element) {
642 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
644 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_html rendered_html");
643 toinsert.append(html);
645 toinsert.append(html);
644 element.append(toinsert);
646 element.append(toinsert);
645 };
647 };
646
648
647
649
648 CodeCell.prototype.append_text = function (data, element, extra_class) {
650 CodeCell.prototype.append_text = function (data, element, extra_class) {
649 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
651 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_text");
652 // escape ANSI & HTML specials in plaintext:
653 data = utils.fixConsole(data);
650 if (extra_class){
654 if (extra_class){
651 toinsert.addClass(extra_class);
655 toinsert.addClass(extra_class);
652 }
656 }
653 toinsert.append($("<pre/>").html(data));
657 toinsert.append($("<pre/>").html(data));
654 element.append(toinsert);
658 element.append(toinsert);
655 };
659 };
656
660
657
661
658 CodeCell.prototype.append_svg = function (svg, element) {
662 CodeCell.prototype.append_svg = function (svg, element) {
659 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
663 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_svg");
660 toinsert.append(svg);
664 toinsert.append(svg);
661 element.append(toinsert);
665 element.append(toinsert);
662 };
666 };
663
667
664
668
665 CodeCell.prototype.append_png = function (png, element) {
669 CodeCell.prototype.append_png = function (png, element) {
666 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
670 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_png");
667 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
671 toinsert.append($("<img/>").attr('src','data:image/png;base64,'+png));
668 element.append(toinsert);
672 element.append(toinsert);
669 };
673 };
670
674
671
675
672 CodeCell.prototype.append_jpeg = function (jpeg, element) {
676 CodeCell.prototype.append_jpeg = function (jpeg, element) {
673 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
677 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_jpeg");
674 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
678 toinsert.append($("<img/>").attr('src','data:image/jpeg;base64,'+jpeg));
675 element.append(toinsert);
679 element.append(toinsert);
676 };
680 };
677
681
678
682
679 CodeCell.prototype.append_latex = function (latex, element) {
683 CodeCell.prototype.append_latex = function (latex, element) {
680 // This method cannot do the typesetting because the latex first has to
684 // This method cannot do the typesetting because the latex first has to
681 // be on the page.
685 // be on the page.
682 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
686 var toinsert = $("<div/>").addClass("box_flex1 output_subarea output_latex");
683 toinsert.append(latex);
687 toinsert.append(latex);
684 element.append(toinsert);
688 element.append(toinsert);
685 };
689 };
686
690
687
691
688 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
692 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
689 var output_div = this.element.find("div.output");
693 var output_div = this.element.find("div.output");
690 if (stdout && stderr && other){
694 if (stdout && stderr && other){
691 // clear all, no need for logic
695 // clear all, no need for logic
692 output_div.html("");
696 output_div.html("");
693 this.outputs = [];
697 this.outputs = [];
694 return;
698 return;
695 }
699 }
696 // remove html output
700 // remove html output
697 // each output_subarea that has an identifying class is in an output_area
701 // each output_subarea that has an identifying class is in an output_area
698 // which is the element to be removed.
702 // which is the element to be removed.
699 if (stdout){
703 if (stdout){
700 output_div.find("div.output_stdout").parent().remove();
704 output_div.find("div.output_stdout").parent().remove();
701 }
705 }
702 if (stderr){
706 if (stderr){
703 output_div.find("div.output_stderr").parent().remove();
707 output_div.find("div.output_stderr").parent().remove();
704 }
708 }
705 if (other){
709 if (other){
706 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
710 output_div.find("div.output_subarea").not("div.output_stderr").not("div.output_stdout").parent().remove();
707 }
711 }
708
712
709 // remove cleared outputs from JSON list:
713 // remove cleared outputs from JSON list:
710 for (var i = this.outputs.length - 1; i >= 0; i--){
714 for (var i = this.outputs.length - 1; i >= 0; i--){
711 var out = this.outputs[i];
715 var out = this.outputs[i];
712 var output_type = out.output_type;
716 var output_type = out.output_type;
713 if (output_type == "display_data" && other){
717 if (output_type == "display_data" && other){
714 this.outputs.splice(i,1);
718 this.outputs.splice(i,1);
715 }else if (output_type == "stream"){
719 }else if (output_type == "stream"){
716 if (stdout && out.stream == "stdout"){
720 if (stdout && out.stream == "stdout"){
717 this.outputs.splice(i,1);
721 this.outputs.splice(i,1);
718 }else if (stderr && out.stream == "stderr"){
722 }else if (stderr && out.stream == "stderr"){
719 this.outputs.splice(i,1);
723 this.outputs.splice(i,1);
720 }
724 }
721 }
725 }
722 }
726 }
723 };
727 };
724
728
725
729
726 CodeCell.prototype.clear_input = function () {
730 CodeCell.prototype.clear_input = function () {
727 this.code_mirror.setValue('');
731 this.code_mirror.setValue('');
728 };
732 };
729
733
730
734
731 CodeCell.prototype.collapse = function () {
735 CodeCell.prototype.collapse = function () {
732 if (!this.collapsed) {
736 if (!this.collapsed) {
733 this.element.find('div.output').hide();
737 this.element.find('div.output').hide();
734 this.collapsed = true;
738 this.collapsed = true;
735 };
739 };
736 };
740 };
737
741
738
742
739 CodeCell.prototype.expand = function () {
743 CodeCell.prototype.expand = function () {
740 if (this.collapsed) {
744 if (this.collapsed) {
741 this.element.find('div.output').show();
745 this.element.find('div.output').show();
742 this.collapsed = false;
746 this.collapsed = false;
743 };
747 };
744 };
748 };
745
749
746
750
747 CodeCell.prototype.toggle_output = function () {
751 CodeCell.prototype.toggle_output = function () {
748 if (this.collapsed) {
752 if (this.collapsed) {
749 this.expand();
753 this.expand();
750 } else {
754 } else {
751 this.collapse();
755 this.collapse();
752 };
756 };
753 };
757 };
754
758
755 CodeCell.prototype.set_input_prompt = function (number) {
759 CodeCell.prototype.set_input_prompt = function (number) {
756 var n = number || '&nbsp;';
760 var n = number || '&nbsp;';
757 this.input_prompt_number = n;
761 this.input_prompt_number = n;
758 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
762 this.element.find('div.input_prompt').html('In&nbsp;[' + n + ']:');
759 };
763 };
760
764
761
765
762 CodeCell.prototype.get_code = function () {
766 CodeCell.prototype.get_code = function () {
763 return this.code_mirror.getValue();
767 return this.code_mirror.getValue();
764 };
768 };
765
769
766
770
767 CodeCell.prototype.set_code = function (code) {
771 CodeCell.prototype.set_code = function (code) {
768 return this.code_mirror.setValue(code);
772 return this.code_mirror.setValue(code);
769 };
773 };
770
774
771
775
772 CodeCell.prototype.at_top = function () {
776 CodeCell.prototype.at_top = function () {
773 var cursor = this.code_mirror.getCursor();
777 var cursor = this.code_mirror.getCursor();
774 if (cursor.line === 0) {
778 if (cursor.line === 0) {
775 return true;
779 return true;
776 } else {
780 } else {
777 return false;
781 return false;
778 }
782 }
779 };
783 };
780
784
781
785
782 CodeCell.prototype.at_bottom = function () {
786 CodeCell.prototype.at_bottom = function () {
783 var cursor = this.code_mirror.getCursor();
787 var cursor = this.code_mirror.getCursor();
784 if (cursor.line === (this.code_mirror.lineCount()-1)) {
788 if (cursor.line === (this.code_mirror.lineCount()-1)) {
785 return true;
789 return true;
786 } else {
790 } else {
787 return false;
791 return false;
788 }
792 }
789 };
793 };
790
794
791
795
792 CodeCell.prototype.fromJSON = function (data) {
796 CodeCell.prototype.fromJSON = function (data) {
793 console.log('Import from JSON:', data);
797 console.log('Import from JSON:', data);
794 if (data.cell_type === 'code') {
798 if (data.cell_type === 'code') {
795 if (data.input !== undefined) {
799 if (data.input !== undefined) {
796 this.set_code(data.input);
800 this.set_code(data.input);
797 }
801 }
798 if (data.prompt_number !== undefined) {
802 if (data.prompt_number !== undefined) {
799 this.set_input_prompt(data.prompt_number);
803 this.set_input_prompt(data.prompt_number);
800 } else {
804 } else {
801 this.set_input_prompt();
805 this.set_input_prompt();
802 };
806 };
803 var len = data.outputs.length;
807 var len = data.outputs.length;
804 for (var i=0; i<len; i++) {
808 for (var i=0; i<len; i++) {
805 this.append_output(data.outputs[i]);
809 this.append_output(data.outputs[i]);
806 };
810 };
807 if (data.collapsed !== undefined) {
811 if (data.collapsed !== undefined) {
808 if (data.collapsed) {
812 if (data.collapsed) {
809 this.collapse();
813 this.collapse();
810 };
814 };
811 };
815 };
812 };
816 };
813 };
817 };
814
818
815
819
816 CodeCell.prototype.toJSON = function () {
820 CodeCell.prototype.toJSON = function () {
817 var data = {};
821 var data = {};
818 data.input = this.get_code();
822 data.input = this.get_code();
819 data.cell_type = 'code';
823 data.cell_type = 'code';
820 if (this.input_prompt_number !== ' ') {
824 if (this.input_prompt_number !== ' ') {
821 data.prompt_number = this.input_prompt_number;
825 data.prompt_number = this.input_prompt_number;
822 };
826 };
823 var outputs = [];
827 var outputs = [];
824 var len = this.outputs.length;
828 var len = this.outputs.length;
825 for (var i=0; i<len; i++) {
829 for (var i=0; i<len; i++) {
826 outputs[i] = this.outputs[i];
830 outputs[i] = this.outputs[i];
827 };
831 };
828 data.outputs = outputs;
832 data.outputs = outputs;
829 data.language = 'python';
833 data.language = 'python';
830 data.collapsed = this.collapsed;
834 data.collapsed = this.collapsed;
831 // console.log('Export to JSON:',data);
835 // console.log('Export to JSON:',data);
832 return data;
836 return data;
833 };
837 };
834
838
835
839
836 IPython.CodeCell = CodeCell;
840 IPython.CodeCell = CodeCell;
837
841
838 return IPython;
842 return IPython;
839 }(IPython));
843 }(IPython));
@@ -1,1080 +1,1076 b''
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.dirty = false;
23 this.dirty = false;
24 this.msg_cell_map = {};
24 this.msg_cell_map = {};
25 this.metadata = {};
25 this.metadata = {};
26 this.control_key_active = false;
26 this.control_key_active = false;
27 this.style();
27 this.style();
28 this.create_elements();
28 this.create_elements();
29 this.bind_events();
29 this.bind_events();
30 this.set_tooltipontab(true);
30 this.set_tooltipontab(true);
31 this.set_smartcompleter(true);
31 this.set_smartcompleter(true);
32 this.set_timebeforetooltip(1200);
32 this.set_timebeforetooltip(1200);
33 };
33 };
34
34
35
35
36 Notebook.prototype.style = function () {
36 Notebook.prototype.style = function () {
37 $('div#notebook').addClass('border-box-sizing');
37 $('div#notebook').addClass('border-box-sizing');
38 };
38 };
39
39
40
40
41 Notebook.prototype.create_elements = function () {
41 Notebook.prototype.create_elements = function () {
42 // We add this end_space div to the end of the notebook div to:
42 // We add this end_space div to the end of the notebook div to:
43 // i) provide a margin between the last cell and the end of the notebook
43 // i) provide a margin between the last cell and the end of the notebook
44 // ii) to prevent the div from scrolling up when the last cell is being
44 // ii) to prevent the div from scrolling up when the last cell is being
45 // edited, but is too low on the page, which browsers will do automatically.
45 // edited, but is too low on the page, which browsers will do automatically.
46 var that = this;
46 var that = this;
47 var end_space = $('<div class="end_space"></div>').height("30%");
47 var end_space = $('<div class="end_space"></div>').height("30%");
48 end_space.dblclick(function (e) {
48 end_space.dblclick(function (e) {
49 if (that.read_only) return;
49 if (that.read_only) return;
50 var ncells = that.ncells();
50 var ncells = that.ncells();
51 that.insert_code_cell_below(ncells-1);
51 that.insert_code_cell_below(ncells-1);
52 });
52 });
53 this.element.append(end_space);
53 this.element.append(end_space);
54 $('div#notebook').addClass('border-box-sizing');
54 $('div#notebook').addClass('border-box-sizing');
55 };
55 };
56
56
57
57
58 Notebook.prototype.bind_events = function () {
58 Notebook.prototype.bind_events = function () {
59 var that = this;
59 var that = this;
60 $(document).keydown(function (event) {
60 $(document).keydown(function (event) {
61 // console.log(event);
61 // console.log(event);
62 if (that.read_only) return true;
62 if (that.read_only) return true;
63 if (event.which === 27) {
63 if (event.which === 27) {
64 // Intercept escape at highest level to avoid closing
64 // Intercept escape at highest level to avoid closing
65 // websocket connection with firefox
65 // websocket connection with firefox
66 event.preventDefault();
66 event.preventDefault();
67 }
67 }
68 if (event.which === 38 && !event.shiftKey) {
68 if (event.which === 38 && !event.shiftKey) {
69 var cell = that.selected_cell();
69 var cell = that.selected_cell();
70 if (cell.at_top()) {
70 if (cell.at_top()) {
71 event.preventDefault();
71 event.preventDefault();
72 that.select_prev();
72 that.select_prev();
73 };
73 };
74 } else if (event.which === 40 && !event.shiftKey) {
74 } else if (event.which === 40 && !event.shiftKey) {
75 var cell = that.selected_cell();
75 var cell = that.selected_cell();
76 if (cell.at_bottom()) {
76 if (cell.at_bottom()) {
77 event.preventDefault();
77 event.preventDefault();
78 that.select_next();
78 that.select_next();
79 };
79 };
80 } else if (event.which === 13 && event.shiftKey) {
80 } else if (event.which === 13 && event.shiftKey) {
81 that.execute_selected_cell();
81 that.execute_selected_cell();
82 return false;
82 return false;
83 } else if (event.which === 13 && event.ctrlKey) {
83 } else if (event.which === 13 && event.ctrlKey) {
84 that.execute_selected_cell({terminal:true});
84 that.execute_selected_cell({terminal:true});
85 return false;
85 return false;
86 } else if (event.which === 77 && event.ctrlKey) {
86 } else if (event.which === 77 && event.ctrlKey) {
87 that.control_key_active = true;
87 that.control_key_active = true;
88 return false;
88 return false;
89 } else if (event.which === 68 && that.control_key_active) {
89 } else if (event.which === 68 && that.control_key_active) {
90 // Delete selected cell = d
90 // Delete selected cell = d
91 that.delete_cell();
91 that.delete_cell();
92 that.control_key_active = false;
92 that.control_key_active = false;
93 return false;
93 return false;
94 } else if (event.which === 65 && that.control_key_active) {
94 } else if (event.which === 65 && that.control_key_active) {
95 // Insert code cell above selected = a
95 // Insert code cell above selected = a
96 that.insert_code_cell_above();
96 that.insert_code_cell_above();
97 that.control_key_active = false;
97 that.control_key_active = false;
98 return false;
98 return false;
99 } else if (event.which === 66 && that.control_key_active) {
99 } else if (event.which === 66 && that.control_key_active) {
100 // Insert code cell below selected = b
100 // Insert code cell below selected = b
101 that.insert_code_cell_below();
101 that.insert_code_cell_below();
102 that.control_key_active = false;
102 that.control_key_active = false;
103 return false;
103 return false;
104 } else if (event.which === 67 && that.control_key_active) {
104 } else if (event.which === 67 && that.control_key_active) {
105 // To code = c
105 // To code = c
106 that.to_code();
106 that.to_code();
107 that.control_key_active = false;
107 that.control_key_active = false;
108 return false;
108 return false;
109 } else if (event.which === 77 && that.control_key_active) {
109 } else if (event.which === 77 && that.control_key_active) {
110 // To markdown = m
110 // To markdown = m
111 that.to_markdown();
111 that.to_markdown();
112 that.control_key_active = false;
112 that.control_key_active = false;
113 return false;
113 return false;
114 } else if (event.which === 84 && that.control_key_active) {
114 } else if (event.which === 84 && that.control_key_active) {
115 // Toggle output = t
115 // Toggle output = t
116 that.toggle_output();
116 that.toggle_output();
117 that.control_key_active = false;
117 that.control_key_active = false;
118 return false;
118 return false;
119 } else if (event.which === 83 && that.control_key_active) {
119 } else if (event.which === 83 && that.control_key_active) {
120 // Save notebook = s
120 // Save notebook = s
121 IPython.save_widget.save_notebook();
121 IPython.save_widget.save_notebook();
122 that.control_key_active = false;
122 that.control_key_active = false;
123 return false;
123 return false;
124 } else if (event.which === 74 && that.control_key_active) {
124 } else if (event.which === 74 && that.control_key_active) {
125 // Move cell down = j
125 // Move cell down = j
126 that.move_cell_down();
126 that.move_cell_down();
127 that.control_key_active = false;
127 that.control_key_active = false;
128 return false;
128 return false;
129 } else if (event.which === 75 && that.control_key_active) {
129 } else if (event.which === 75 && that.control_key_active) {
130 // Move cell up = k
130 // Move cell up = k
131 that.move_cell_up();
131 that.move_cell_up();
132 that.control_key_active = false;
132 that.control_key_active = false;
133 return false;
133 return false;
134 } else if (event.which === 80 && that.control_key_active) {
134 } else if (event.which === 80 && that.control_key_active) {
135 // Select previous = p
135 // Select previous = p
136 that.select_prev();
136 that.select_prev();
137 that.control_key_active = false;
137 that.control_key_active = false;
138 return false;
138 return false;
139 } else if (event.which === 78 && that.control_key_active) {
139 } else if (event.which === 78 && that.control_key_active) {
140 // Select next = n
140 // Select next = n
141 that.select_next();
141 that.select_next();
142 that.control_key_active = false;
142 that.control_key_active = false;
143 return false;
143 return false;
144 } else if (event.which === 76 && that.control_key_active) {
144 } else if (event.which === 76 && that.control_key_active) {
145 // Toggle line numbers = l
145 // Toggle line numbers = l
146 that.cell_toggle_line_numbers();
146 that.cell_toggle_line_numbers();
147 that.control_key_active = false;
147 that.control_key_active = false;
148 return false;
148 return false;
149 } else if (event.which === 73 && that.control_key_active) {
149 } else if (event.which === 73 && that.control_key_active) {
150 // Interrupt kernel = i
150 // Interrupt kernel = i
151 IPython.notebook.kernel.interrupt();
151 IPython.notebook.kernel.interrupt();
152 that.control_key_active = false;
152 that.control_key_active = false;
153 return false;
153 return false;
154 } else if (event.which === 190 && that.control_key_active) {
154 } else if (event.which === 190 && that.control_key_active) {
155 // Restart kernel = . # matches qt console
155 // Restart kernel = . # matches qt console
156 IPython.notebook.restart_kernel();
156 IPython.notebook.restart_kernel();
157 that.control_key_active = false;
157 that.control_key_active = false;
158 return false;
158 return false;
159 } else if (event.which === 72 && that.control_key_active) {
159 } else if (event.which === 72 && that.control_key_active) {
160 // Show keyboard shortcuts = h
160 // Show keyboard shortcuts = h
161 that.toggle_keyboard_shortcuts();
161 that.toggle_keyboard_shortcuts();
162 that.control_key_active = false;
162 that.control_key_active = false;
163 return false;
163 return false;
164 } else if (that.control_key_active) {
164 } else if (that.control_key_active) {
165 that.control_key_active = false;
165 that.control_key_active = false;
166 return true;
166 return true;
167 };
167 };
168 return true;
168 return true;
169 });
169 });
170
170
171 this.element.bind('collapse_pager', function () {
171 this.element.bind('collapse_pager', function () {
172 var app_height = $('div#main_app').height(); // content height
172 var app_height = $('div#main_app').height(); // content height
173 var splitter_height = $('div#pager_splitter').outerHeight(true);
173 var splitter_height = $('div#pager_splitter').outerHeight(true);
174 var new_height = app_height - splitter_height;
174 var new_height = app_height - splitter_height;
175 that.element.animate({height : new_height + 'px'}, 'fast');
175 that.element.animate({height : new_height + 'px'}, 'fast');
176 });
176 });
177
177
178 this.element.bind('expand_pager', function () {
178 this.element.bind('expand_pager', function () {
179 var app_height = $('div#main_app').height(); // content height
179 var app_height = $('div#main_app').height(); // content height
180 var splitter_height = $('div#pager_splitter').outerHeight(true);
180 var splitter_height = $('div#pager_splitter').outerHeight(true);
181 var pager_height = $('div#pager').outerHeight(true);
181 var pager_height = $('div#pager').outerHeight(true);
182 var new_height = app_height - pager_height - splitter_height;
182 var new_height = app_height - pager_height - splitter_height;
183 that.element.animate({height : new_height + 'px'}, 'fast');
183 that.element.animate({height : new_height + 'px'}, 'fast');
184 });
184 });
185
185
186 this.element.bind('collapse_left_panel', function () {
186 this.element.bind('collapse_left_panel', function () {
187 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
187 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
188 var new_margin = splitter_width;
188 var new_margin = splitter_width;
189 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
189 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
190 });
190 });
191
191
192 this.element.bind('expand_left_panel', function () {
192 this.element.bind('expand_left_panel', function () {
193 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
193 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
194 var left_panel_width = IPython.left_panel.width;
194 var left_panel_width = IPython.left_panel.width;
195 var new_margin = splitter_width + left_panel_width;
195 var new_margin = splitter_width + left_panel_width;
196 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
196 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
197 });
197 });
198
198
199 $(window).bind('beforeunload', function () {
199 $(window).bind('beforeunload', function () {
200 var kill_kernel = $('#kill_kernel').prop('checked');
200 var kill_kernel = $('#kill_kernel').prop('checked');
201 if (kill_kernel) {
201 if (kill_kernel) {
202 that.kernel.kill();
202 that.kernel.kill();
203 }
203 }
204 if (that.dirty && ! that.read_only) {
204 if (that.dirty && ! that.read_only) {
205 return "You have unsaved changes that will be lost if you leave this page.";
205 return "You have unsaved changes that will be lost if you leave this page.";
206 };
206 };
207 // Null is the *only* return value that will make the browser not
207 // Null is the *only* return value that will make the browser not
208 // pop up the "don't leave" dialog.
208 // pop up the "don't leave" dialog.
209 return null;
209 return null;
210 });
210 });
211 };
211 };
212
212
213
213
214 Notebook.prototype.toggle_keyboard_shortcuts = function () {
214 Notebook.prototype.toggle_keyboard_shortcuts = function () {
215 // toggles display of keyboard shortcut dialog
215 // toggles display of keyboard shortcut dialog
216 var that = this;
216 var that = this;
217 if ( this.shortcut_dialog ){
217 if ( this.shortcut_dialog ){
218 // if dialog is already shown, close it
218 // if dialog is already shown, close it
219 this.shortcut_dialog.dialog("close");
219 this.shortcut_dialog.dialog("close");
220 this.shortcut_dialog = null;
220 this.shortcut_dialog = null;
221 return;
221 return;
222 }
222 }
223 var dialog = $('<div/>');
223 var dialog = $('<div/>');
224 this.shortcut_dialog = dialog;
224 this.shortcut_dialog = dialog;
225 var shortcuts = [
225 var shortcuts = [
226 {key: 'Shift-Enter', help: 'run cell'},
226 {key: 'Shift-Enter', help: 'run cell'},
227 {key: 'Ctrl-Enter', help: 'run cell in-place'},
227 {key: 'Ctrl-Enter', help: 'run cell in-place'},
228 {key: 'Ctrl-m d', help: 'delete cell'},
228 {key: 'Ctrl-m d', help: 'delete cell'},
229 {key: 'Ctrl-m a', help: 'insert cell above'},
229 {key: 'Ctrl-m a', help: 'insert cell above'},
230 {key: 'Ctrl-m b', help: 'insert cell below'},
230 {key: 'Ctrl-m b', help: 'insert cell below'},
231 {key: 'Ctrl-m t', help: 'toggle output'},
231 {key: 'Ctrl-m t', help: 'toggle output'},
232 {key: 'Ctrl-m l', help: 'toggle line numbers'},
232 {key: 'Ctrl-m l', help: 'toggle line numbers'},
233 {key: 'Ctrl-m s', help: 'save notebook'},
233 {key: 'Ctrl-m s', help: 'save notebook'},
234 {key: 'Ctrl-m j', help: 'move cell down'},
234 {key: 'Ctrl-m j', help: 'move cell down'},
235 {key: 'Ctrl-m k', help: 'move cell up'},
235 {key: 'Ctrl-m k', help: 'move cell up'},
236 {key: 'Ctrl-m c', help: 'code cell'},
236 {key: 'Ctrl-m c', help: 'code cell'},
237 {key: 'Ctrl-m m', help: 'markdown cell'},
237 {key: 'Ctrl-m m', help: 'markdown cell'},
238 {key: 'Ctrl-m p', help: 'select previous'},
238 {key: 'Ctrl-m p', help: 'select previous'},
239 {key: 'Ctrl-m n', help: 'select next'},
239 {key: 'Ctrl-m n', help: 'select next'},
240 {key: 'Ctrl-m i', help: 'interrupt kernel'},
240 {key: 'Ctrl-m i', help: 'interrupt kernel'},
241 {key: 'Ctrl-m .', help: 'restart kernel'},
241 {key: 'Ctrl-m .', help: 'restart kernel'},
242 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
242 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
243 ];
243 ];
244 for (var i=0; i<shortcuts.length; i++) {
244 for (var i=0; i<shortcuts.length; i++) {
245 dialog.append($('<div>').
245 dialog.append($('<div>').
246 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
246 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
247 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
247 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
248 );
248 );
249 };
249 };
250 dialog.bind('dialogclose', function(event) {
250 dialog.bind('dialogclose', function(event) {
251 // dialog has been closed, allow it to be drawn again.
251 // dialog has been closed, allow it to be drawn again.
252 that.shortcut_dialog = null;
252 that.shortcut_dialog = null;
253 });
253 });
254 dialog.dialog({title: 'Keyboard shortcuts'});
254 dialog.dialog({title: 'Keyboard shortcuts'});
255 };
255 };
256
256
257
257
258 Notebook.prototype.scroll_to_bottom = function () {
258 Notebook.prototype.scroll_to_bottom = function () {
259 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
259 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
260 };
260 };
261
261
262
262
263 Notebook.prototype.scroll_to_top = function () {
263 Notebook.prototype.scroll_to_top = function () {
264 this.element.animate({scrollTop:0}, 0);
264 this.element.animate({scrollTop:0}, 0);
265 };
265 };
266
266
267
267
268 // Cell indexing, retrieval, etc.
268 // Cell indexing, retrieval, etc.
269
269
270
270
271 Notebook.prototype.cell_elements = function () {
271 Notebook.prototype.cell_elements = function () {
272 return this.element.children("div.cell");
272 return this.element.children("div.cell");
273 };
273 };
274
274
275
275
276 Notebook.prototype.ncells = function (cell) {
276 Notebook.prototype.ncells = function (cell) {
277 return this.cell_elements().length;
277 return this.cell_elements().length;
278 };
278 };
279
279
280
280
281 // TODO: we are often calling cells as cells()[i], which we should optimize
281 // TODO: we are often calling cells as cells()[i], which we should optimize
282 // to cells(i) or a new method.
282 // to cells(i) or a new method.
283 Notebook.prototype.cells = function () {
283 Notebook.prototype.cells = function () {
284 return this.cell_elements().toArray().map(function (e) {
284 return this.cell_elements().toArray().map(function (e) {
285 return $(e).data("cell");
285 return $(e).data("cell");
286 });
286 });
287 };
287 };
288
288
289
289
290 Notebook.prototype.find_cell_index = function (cell) {
290 Notebook.prototype.find_cell_index = function (cell) {
291 var result = null;
291 var result = null;
292 this.cell_elements().filter(function (index) {
292 this.cell_elements().filter(function (index) {
293 if ($(this).data("cell") === cell) {
293 if ($(this).data("cell") === cell) {
294 result = index;
294 result = index;
295 };
295 };
296 });
296 });
297 return result;
297 return result;
298 };
298 };
299
299
300
300
301 Notebook.prototype.index_or_selected = function (index) {
301 Notebook.prototype.index_or_selected = function (index) {
302 return index || this.selected_index() || 0;
302 return index || this.selected_index() || 0;
303 };
303 };
304
304
305
305
306 Notebook.prototype.select = function (index) {
306 Notebook.prototype.select = function (index) {
307 if (index !== undefined && index >= 0 && index < this.ncells()) {
307 if (index !== undefined && index >= 0 && index < this.ncells()) {
308 if (this.selected_index() !== null) {
308 if (this.selected_index() !== null) {
309 this.selected_cell().unselect();
309 this.selected_cell().unselect();
310 };
310 };
311 this.cells()[index].select();
311 this.cells()[index].select();
312 };
312 };
313 return this;
313 return this;
314 };
314 };
315
315
316
316
317 Notebook.prototype.select_next = function () {
317 Notebook.prototype.select_next = function () {
318 var index = this.selected_index();
318 var index = this.selected_index();
319 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
319 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
320 this.select(index+1);
320 this.select(index+1);
321 };
321 };
322 return this;
322 return this;
323 };
323 };
324
324
325
325
326 Notebook.prototype.select_prev = function () {
326 Notebook.prototype.select_prev = function () {
327 var index = this.selected_index();
327 var index = this.selected_index();
328 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
328 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
329 this.select(index-1);
329 this.select(index-1);
330 };
330 };
331 return this;
331 return this;
332 };
332 };
333
333
334
334
335 Notebook.prototype.selected_index = function () {
335 Notebook.prototype.selected_index = function () {
336 var result = null;
336 var result = null;
337 this.cell_elements().filter(function (index) {
337 this.cell_elements().filter(function (index) {
338 if ($(this).data("cell").selected === true) {
338 if ($(this).data("cell").selected === true) {
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.cell_for_msg = function (msg_id) {
346 Notebook.prototype.cell_for_msg = function (msg_id) {
347 var cell_id = this.msg_cell_map[msg_id];
347 var cell_id = this.msg_cell_map[msg_id];
348 var result = null;
348 var result = null;
349 this.cell_elements().filter(function (index) {
349 this.cell_elements().filter(function (index) {
350 cell = $(this).data("cell");
350 cell = $(this).data("cell");
351 if (cell.cell_id === cell_id) {
351 if (cell.cell_id === cell_id) {
352 result = cell;
352 result = cell;
353 };
353 };
354 });
354 });
355 return result;
355 return result;
356 };
356 };
357
357
358
358
359 Notebook.prototype.selected_cell = function () {
359 Notebook.prototype.selected_cell = function () {
360 return this.cell_elements().eq(this.selected_index()).data("cell");
360 return this.cell_elements().eq(this.selected_index()).data("cell");
361 };
361 };
362
362
363
363
364 // Cell insertion, deletion and moving.
364 // Cell insertion, deletion and moving.
365
365
366
366
367 Notebook.prototype.delete_cell = function (index) {
367 Notebook.prototype.delete_cell = function (index) {
368 var i = index || this.selected_index();
368 var i = index || this.selected_index();
369 if (i !== null && i >= 0 && i < this.ncells()) {
369 if (i !== null && i >= 0 && i < this.ncells()) {
370 this.cell_elements().eq(i).remove();
370 this.cell_elements().eq(i).remove();
371 if (i === (this.ncells())) {
371 if (i === (this.ncells())) {
372 this.select(i-1);
372 this.select(i-1);
373 } else {
373 } else {
374 this.select(i);
374 this.select(i);
375 };
375 };
376 };
376 };
377 this.dirty = true;
377 this.dirty = true;
378 return this;
378 return this;
379 };
379 };
380
380
381
381
382 Notebook.prototype.append_cell = function (cell) {
382 Notebook.prototype.append_cell = function (cell) {
383 this.element.find('div.end_space').before(cell.element);
383 this.element.find('div.end_space').before(cell.element);
384 this.dirty = true;
384 this.dirty = true;
385 return this;
385 return this;
386 };
386 };
387
387
388
388
389 Notebook.prototype.insert_cell_below = function (cell, index) {
389 Notebook.prototype.insert_cell_below = function (cell, index) {
390 var ncells = this.ncells();
390 var ncells = this.ncells();
391 if (ncells === 0) {
391 if (ncells === 0) {
392 this.append_cell(cell);
392 this.append_cell(cell);
393 return this;
393 return this;
394 };
394 };
395 if (index >= 0 && index < ncells) {
395 if (index >= 0 && index < ncells) {
396 this.cell_elements().eq(index).after(cell.element);
396 this.cell_elements().eq(index).after(cell.element);
397 };
397 };
398 this.dirty = true;
398 this.dirty = true;
399 return this;
399 return this;
400 };
400 };
401
401
402
402
403 Notebook.prototype.insert_cell_above = function (cell, index) {
403 Notebook.prototype.insert_cell_above = function (cell, index) {
404 var ncells = this.ncells();
404 var ncells = this.ncells();
405 if (ncells === 0) {
405 if (ncells === 0) {
406 this.append_cell(cell);
406 this.append_cell(cell);
407 return this;
407 return this;
408 };
408 };
409 if (index >= 0 && index < ncells) {
409 if (index >= 0 && index < ncells) {
410 this.cell_elements().eq(index).before(cell.element);
410 this.cell_elements().eq(index).before(cell.element);
411 };
411 };
412 this.dirty = true;
412 this.dirty = true;
413 return this;
413 return this;
414 };
414 };
415
415
416
416
417 Notebook.prototype.move_cell_up = function (index) {
417 Notebook.prototype.move_cell_up = function (index) {
418 var i = index || this.selected_index();
418 var i = index || this.selected_index();
419 if (i !== null && i < this.ncells() && i > 0) {
419 if (i !== null && i < this.ncells() && i > 0) {
420 var pivot = this.cell_elements().eq(i-1);
420 var pivot = this.cell_elements().eq(i-1);
421 var tomove = this.cell_elements().eq(i);
421 var tomove = this.cell_elements().eq(i);
422 if (pivot !== null && tomove !== null) {
422 if (pivot !== null && tomove !== null) {
423 tomove.detach();
423 tomove.detach();
424 pivot.before(tomove);
424 pivot.before(tomove);
425 this.select(i-1);
425 this.select(i-1);
426 };
426 };
427 };
427 };
428 this.dirty = true;
428 this.dirty = true;
429 return this;
429 return this;
430 };
430 };
431
431
432
432
433 Notebook.prototype.move_cell_down = function (index) {
433 Notebook.prototype.move_cell_down = function (index) {
434 var i = index || this.selected_index();
434 var i = index || this.selected_index();
435 if (i !== null && i < (this.ncells()-1) && i >= 0) {
435 if (i !== null && i < (this.ncells()-1) && i >= 0) {
436 var pivot = this.cell_elements().eq(i+1);
436 var pivot = this.cell_elements().eq(i+1);
437 var tomove = this.cell_elements().eq(i);
437 var tomove = this.cell_elements().eq(i);
438 if (pivot !== null && tomove !== null) {
438 if (pivot !== null && tomove !== null) {
439 tomove.detach();
439 tomove.detach();
440 pivot.after(tomove);
440 pivot.after(tomove);
441 this.select(i+1);
441 this.select(i+1);
442 };
442 };
443 };
443 };
444 this.dirty = true;
444 this.dirty = true;
445 return this;
445 return this;
446 };
446 };
447
447
448
448
449 Notebook.prototype.sort_cells = function () {
449 Notebook.prototype.sort_cells = function () {
450 var ncells = this.ncells();
450 var ncells = this.ncells();
451 var sindex = this.selected_index();
451 var sindex = this.selected_index();
452 var swapped;
452 var swapped;
453 do {
453 do {
454 swapped = false;
454 swapped = false;
455 for (var i=1; i<ncells; i++) {
455 for (var i=1; i<ncells; i++) {
456 current = this.cell_elements().eq(i).data("cell");
456 current = this.cell_elements().eq(i).data("cell");
457 previous = this.cell_elements().eq(i-1).data("cell");
457 previous = this.cell_elements().eq(i-1).data("cell");
458 if (previous.input_prompt_number > current.input_prompt_number) {
458 if (previous.input_prompt_number > current.input_prompt_number) {
459 this.move_cell_up(i);
459 this.move_cell_up(i);
460 swapped = true;
460 swapped = true;
461 };
461 };
462 };
462 };
463 } while (swapped);
463 } while (swapped);
464 this.select(sindex);
464 this.select(sindex);
465 return this;
465 return this;
466 };
466 };
467
467
468
468
469 Notebook.prototype.insert_code_cell_above = function (index) {
469 Notebook.prototype.insert_code_cell_above = function (index) {
470 // TODO: Bounds check for i
470 // TODO: Bounds check for i
471 var i = this.index_or_selected(index);
471 var i = this.index_or_selected(index);
472 var cell = new IPython.CodeCell(this);
472 var cell = new IPython.CodeCell(this);
473 cell.set_input_prompt();
473 cell.set_input_prompt();
474 this.insert_cell_above(cell, i);
474 this.insert_cell_above(cell, i);
475 this.select(this.find_cell_index(cell));
475 this.select(this.find_cell_index(cell));
476 return cell;
476 return cell;
477 };
477 };
478
478
479
479
480 Notebook.prototype.insert_code_cell_below = function (index) {
480 Notebook.prototype.insert_code_cell_below = function (index) {
481 // TODO: Bounds check for i
481 // TODO: Bounds check for i
482 var i = this.index_or_selected(index);
482 var i = this.index_or_selected(index);
483 var cell = new IPython.CodeCell(this);
483 var cell = new IPython.CodeCell(this);
484 cell.set_input_prompt();
484 cell.set_input_prompt();
485 this.insert_cell_below(cell, i);
485 this.insert_cell_below(cell, i);
486 this.select(this.find_cell_index(cell));
486 this.select(this.find_cell_index(cell));
487 return cell;
487 return cell;
488 };
488 };
489
489
490
490
491 Notebook.prototype.insert_html_cell_above = function (index) {
491 Notebook.prototype.insert_html_cell_above = function (index) {
492 // TODO: Bounds check for i
492 // TODO: Bounds check for i
493 var i = this.index_or_selected(index);
493 var i = this.index_or_selected(index);
494 var cell = new IPython.HTMLCell(this);
494 var cell = new IPython.HTMLCell(this);
495 cell.config_mathjax();
495 cell.config_mathjax();
496 this.insert_cell_above(cell, i);
496 this.insert_cell_above(cell, i);
497 this.select(this.find_cell_index(cell));
497 this.select(this.find_cell_index(cell));
498 return cell;
498 return cell;
499 };
499 };
500
500
501
501
502 Notebook.prototype.insert_html_cell_below = function (index) {
502 Notebook.prototype.insert_html_cell_below = function (index) {
503 // TODO: Bounds check for i
503 // TODO: Bounds check for i
504 var i = this.index_or_selected(index);
504 var i = this.index_or_selected(index);
505 var cell = new IPython.HTMLCell(this);
505 var cell = new IPython.HTMLCell(this);
506 cell.config_mathjax();
506 cell.config_mathjax();
507 this.insert_cell_below(cell, i);
507 this.insert_cell_below(cell, i);
508 this.select(this.find_cell_index(cell));
508 this.select(this.find_cell_index(cell));
509 return cell;
509 return cell;
510 };
510 };
511
511
512
512
513 Notebook.prototype.insert_markdown_cell_above = function (index) {
513 Notebook.prototype.insert_markdown_cell_above = function (index) {
514 // TODO: Bounds check for i
514 // TODO: Bounds check for i
515 var i = this.index_or_selected(index);
515 var i = this.index_or_selected(index);
516 var cell = new IPython.MarkdownCell(this);
516 var cell = new IPython.MarkdownCell(this);
517 cell.config_mathjax();
517 cell.config_mathjax();
518 this.insert_cell_above(cell, i);
518 this.insert_cell_above(cell, i);
519 this.select(this.find_cell_index(cell));
519 this.select(this.find_cell_index(cell));
520 return cell;
520 return cell;
521 };
521 };
522
522
523
523
524 Notebook.prototype.insert_markdown_cell_below = function (index) {
524 Notebook.prototype.insert_markdown_cell_below = function (index) {
525 // TODO: Bounds check for i
525 // TODO: Bounds check for i
526 var i = this.index_or_selected(index);
526 var i = this.index_or_selected(index);
527 var cell = new IPython.MarkdownCell(this);
527 var cell = new IPython.MarkdownCell(this);
528 cell.config_mathjax();
528 cell.config_mathjax();
529 this.insert_cell_below(cell, i);
529 this.insert_cell_below(cell, i);
530 this.select(this.find_cell_index(cell));
530 this.select(this.find_cell_index(cell));
531 return cell;
531 return cell;
532 };
532 };
533
533
534
534
535 Notebook.prototype.to_code = function (index) {
535 Notebook.prototype.to_code = function (index) {
536 // TODO: Bounds check for i
536 // TODO: Bounds check for i
537 var i = this.index_or_selected(index);
537 var i = this.index_or_selected(index);
538 var source_element = this.cell_elements().eq(i);
538 var source_element = this.cell_elements().eq(i);
539 var source_cell = source_element.data("cell");
539 var source_cell = source_element.data("cell");
540 if (source_cell instanceof IPython.HTMLCell ||
540 if (source_cell instanceof IPython.HTMLCell ||
541 source_cell instanceof IPython.MarkdownCell) {
541 source_cell instanceof IPython.MarkdownCell) {
542 this.insert_code_cell_below(i);
542 this.insert_code_cell_below(i);
543 var target_cell = this.cells()[i+1];
543 var target_cell = this.cells()[i+1];
544 target_cell.set_code(source_cell.get_source());
544 target_cell.set_code(source_cell.get_source());
545 source_element.remove();
545 source_element.remove();
546 target_cell.select();
546 target_cell.select();
547 };
547 };
548 this.dirty = true;
548 this.dirty = true;
549 };
549 };
550
550
551
551
552 Notebook.prototype.to_markdown = function (index) {
552 Notebook.prototype.to_markdown = function (index) {
553 // TODO: Bounds check for i
553 // TODO: Bounds check for i
554 var i = this.index_or_selected(index);
554 var i = this.index_or_selected(index);
555 var source_element = this.cell_elements().eq(i);
555 var source_element = this.cell_elements().eq(i);
556 var source_cell = source_element.data("cell");
556 var source_cell = source_element.data("cell");
557 var target_cell = null;
557 var target_cell = null;
558 if (source_cell instanceof IPython.CodeCell) {
558 if (source_cell instanceof IPython.CodeCell) {
559 this.insert_markdown_cell_below(i);
559 this.insert_markdown_cell_below(i);
560 target_cell = this.cells()[i+1];
560 target_cell = this.cells()[i+1];
561 var text = source_cell.get_code();
561 var text = source_cell.get_code();
562 } else if (source_cell instanceof IPython.HTMLCell) {
562 } else if (source_cell instanceof IPython.HTMLCell) {
563 this.insert_markdown_cell_below(i);
563 this.insert_markdown_cell_below(i);
564 target_cell = this.cells()[i+1];
564 target_cell = this.cells()[i+1];
565 var text = source_cell.get_source();
565 var text = source_cell.get_source();
566 if (text === source_cell.placeholder) {
566 if (text === source_cell.placeholder) {
567 text = target_cell.placeholder;
567 text = target_cell.placeholder;
568 }
568 }
569 }
569 }
570 if (target_cell !== null) {
570 if (target_cell !== null) {
571 if (text === "") {text = target_cell.placeholder;};
571 if (text === "") {text = target_cell.placeholder;};
572 target_cell.set_source(text);
572 target_cell.set_source(text);
573 source_element.remove();
573 source_element.remove();
574 target_cell.edit();
574 target_cell.edit();
575 }
575 }
576 this.dirty = true;
576 this.dirty = true;
577 };
577 };
578
578
579
579
580 Notebook.prototype.to_html = function (index) {
580 Notebook.prototype.to_html = function (index) {
581 // TODO: Bounds check for i
581 // TODO: Bounds check for i
582 var i = this.index_or_selected(index);
582 var i = this.index_or_selected(index);
583 var source_element = this.cell_elements().eq(i);
583 var source_element = this.cell_elements().eq(i);
584 var source_cell = source_element.data("cell");
584 var source_cell = source_element.data("cell");
585 var target_cell = null;
585 var target_cell = null;
586 if (source_cell instanceof IPython.CodeCell) {
586 if (source_cell instanceof IPython.CodeCell) {
587 this.insert_html_cell_below(i);
587 this.insert_html_cell_below(i);
588 target_cell = this.cells()[i+1];
588 target_cell = this.cells()[i+1];
589 var text = source_cell.get_code();
589 var text = source_cell.get_code();
590 } else if (source_cell instanceof IPython.MarkdownCell) {
590 } else if (source_cell instanceof IPython.MarkdownCell) {
591 this.insert_html_cell_below(i);
591 this.insert_html_cell_below(i);
592 target_cell = this.cells()[i+1];
592 target_cell = this.cells()[i+1];
593 var text = source_cell.get_source();
593 var text = source_cell.get_source();
594 if (text === source_cell.placeholder) {
594 if (text === source_cell.placeholder) {
595 text = target_cell.placeholder;
595 text = target_cell.placeholder;
596 }
596 }
597 }
597 }
598 if (target_cell !== null) {
598 if (target_cell !== null) {
599 if (text === "") {text = target_cell.placeholder;};
599 if (text === "") {text = target_cell.placeholder;};
600 target_cell.set_source(text);
600 target_cell.set_source(text);
601 source_element.remove();
601 source_element.remove();
602 target_cell.edit();
602 target_cell.edit();
603 }
603 }
604 this.dirty = true;
604 this.dirty = true;
605 };
605 };
606
606
607
607
608 // Cell collapsing and output clearing
608 // Cell collapsing and output clearing
609
609
610 Notebook.prototype.collapse = function (index) {
610 Notebook.prototype.collapse = function (index) {
611 var i = this.index_or_selected(index);
611 var i = this.index_or_selected(index);
612 this.cells()[i].collapse();
612 this.cells()[i].collapse();
613 this.dirty = true;
613 this.dirty = true;
614 };
614 };
615
615
616
616
617 Notebook.prototype.expand = function (index) {
617 Notebook.prototype.expand = function (index) {
618 var i = this.index_or_selected(index);
618 var i = this.index_or_selected(index);
619 this.cells()[i].expand();
619 this.cells()[i].expand();
620 this.dirty = true;
620 this.dirty = true;
621 };
621 };
622
622
623
623
624 Notebook.prototype.toggle_output = function (index) {
624 Notebook.prototype.toggle_output = function (index) {
625 var i = this.index_or_selected(index);
625 var i = this.index_or_selected(index);
626 this.cells()[i].toggle_output();
626 this.cells()[i].toggle_output();
627 this.dirty = true;
627 this.dirty = true;
628 };
628 };
629
629
630
630
631 Notebook.prototype.set_timebeforetooltip = function (time) {
631 Notebook.prototype.set_timebeforetooltip = function (time) {
632 console.log("change time before tooltip to : "+time);
632 console.log("change time before tooltip to : "+time);
633 this.time_before_tooltip = time;
633 this.time_before_tooltip = time;
634 };
634 };
635
635
636 Notebook.prototype.set_tooltipontab = function (state) {
636 Notebook.prototype.set_tooltipontab = function (state) {
637 console.log("change tooltip on tab to : "+state);
637 console.log("change tooltip on tab to : "+state);
638 this.tooltip_on_tab = state;
638 this.tooltip_on_tab = state;
639 };
639 };
640
640
641 Notebook.prototype.set_smartcompleter = function (state) {
641 Notebook.prototype.set_smartcompleter = function (state) {
642 console.log("Smart completion (kwargs first) changed to to : "+state);
642 console.log("Smart completion (kwargs first) changed to to : "+state);
643 this.smart_completer = state;
643 this.smart_completer = state;
644 };
644 };
645
645
646 Notebook.prototype.set_autoindent = function (state) {
646 Notebook.prototype.set_autoindent = function (state) {
647 var cells = this.cells();
647 var cells = this.cells();
648 len = cells.length;
648 len = cells.length;
649 for (var i=0; i<len; i++) {
649 for (var i=0; i<len; i++) {
650 cells[i].set_autoindent(state);
650 cells[i].set_autoindent(state);
651 };
651 };
652 };
652 };
653
653
654
654
655 Notebook.prototype.clear_all_output = function () {
655 Notebook.prototype.clear_all_output = function () {
656 var ncells = this.ncells();
656 var ncells = this.ncells();
657 var cells = this.cells();
657 var cells = this.cells();
658 for (var i=0; i<ncells; i++) {
658 for (var i=0; i<ncells; i++) {
659 if (cells[i] instanceof IPython.CodeCell) {
659 if (cells[i] instanceof IPython.CodeCell) {
660 cells[i].clear_output(true,true,true);
660 cells[i].clear_output(true,true,true);
661 }
661 }
662 };
662 };
663 this.dirty = true;
663 this.dirty = true;
664 };
664 };
665
665
666 // Other cell functions: line numbers, ...
666 // Other cell functions: line numbers, ...
667
667
668 Notebook.prototype.cell_toggle_line_numbers = function() {
668 Notebook.prototype.cell_toggle_line_numbers = function() {
669 this.selected_cell().toggle_line_numbers();
669 this.selected_cell().toggle_line_numbers();
670 };
670 };
671
671
672 // Kernel related things
672 // Kernel related things
673
673
674 Notebook.prototype.start_kernel = function () {
674 Notebook.prototype.start_kernel = function () {
675 this.kernel = new IPython.Kernel();
675 this.kernel = new IPython.Kernel();
676 var notebook_id = IPython.save_widget.get_notebook_id();
676 var notebook_id = IPython.save_widget.get_notebook_id();
677 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
677 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
678 };
678 };
679
679
680
680
681 Notebook.prototype.restart_kernel = function () {
681 Notebook.prototype.restart_kernel = function () {
682 var that = this;
682 var that = this;
683 var notebook_id = IPython.save_widget.get_notebook_id();
683 var notebook_id = IPython.save_widget.get_notebook_id();
684
684
685 var dialog = $('<div/>');
685 var dialog = $('<div/>');
686 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
686 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
687 $(document).append(dialog);
687 $(document).append(dialog);
688 dialog.dialog({
688 dialog.dialog({
689 resizable: false,
689 resizable: false,
690 modal: true,
690 modal: true,
691 title: "Restart kernel or continue running?",
691 title: "Restart kernel or continue running?",
692 buttons : {
692 buttons : {
693 "Restart": function () {
693 "Restart": function () {
694 that.kernel.restart($.proxy(that.kernel_started, that));
694 that.kernel.restart($.proxy(that.kernel_started, that));
695 $(this).dialog('close');
695 $(this).dialog('close');
696 },
696 },
697 "Continue running": function () {
697 "Continue running": function () {
698 $(this).dialog('close');
698 $(this).dialog('close');
699 }
699 }
700 }
700 }
701 });
701 });
702 };
702 };
703
703
704
704
705 Notebook.prototype.kernel_started = function () {
705 Notebook.prototype.kernel_started = function () {
706 console.log("Kernel started: ", this.kernel.kernel_id);
706 console.log("Kernel started: ", this.kernel.kernel_id);
707 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
707 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
708 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
708 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
709 };
709 };
710
710
711
711
712 Notebook.prototype.handle_shell_reply = function (e) {
712 Notebook.prototype.handle_shell_reply = function (e) {
713 reply = $.parseJSON(e.data);
713 reply = $.parseJSON(e.data);
714 var header = reply.header;
714 var header = reply.header;
715 var content = reply.content;
715 var content = reply.content;
716 var msg_type = header.msg_type;
716 var msg_type = header.msg_type;
717 // console.log(reply);
717 // console.log(reply);
718 var cell = this.cell_for_msg(reply.parent_header.msg_id);
718 var cell = this.cell_for_msg(reply.parent_header.msg_id);
719 if (msg_type === "execute_reply") {
719 if (msg_type === "execute_reply") {
720 cell.set_input_prompt(content.execution_count);
720 cell.set_input_prompt(content.execution_count);
721 cell.element.removeClass("running");
721 cell.element.removeClass("running");
722 this.dirty = true;
722 this.dirty = true;
723 } else if (msg_type === "complete_reply") {
723 } else if (msg_type === "complete_reply") {
724 cell.finish_completing(content.matched_text, content.matches);
724 cell.finish_completing(content.matched_text, content.matches);
725 } else if (msg_type === "object_info_reply"){
725 } else if (msg_type === "object_info_reply"){
726 //console.log('back from object_info_request : ')
726 //console.log('back from object_info_request : ')
727 rep = reply.content;
727 rep = reply.content;
728 if(rep.found)
728 if(rep.found)
729 {
729 {
730 cell.finish_tooltip(rep);
730 cell.finish_tooltip(rep);
731 }
731 }
732 } else {
732 } else {
733 //console.log("unknown reply:"+msg_type);
733 //console.log("unknown reply:"+msg_type);
734 }
734 }
735 // when having a rely from object_info_reply,
735 // when having a rely from object_info_reply,
736 // no payload so no nned to handle it
736 // no payload so no nned to handle it
737 if(typeof(content.payload)!='undefined') {
737 if(typeof(content.payload)!='undefined') {
738 var payload = content.payload || [];
738 var payload = content.payload || [];
739 this.handle_payload(cell, payload);
739 this.handle_payload(cell, payload);
740 }
740 }
741 };
741 };
742
742
743
743
744 Notebook.prototype.handle_payload = function (cell, payload) {
744 Notebook.prototype.handle_payload = function (cell, payload) {
745 var l = payload.length;
745 var l = payload.length;
746 for (var i=0; i<l; i++) {
746 for (var i=0; i<l; i++) {
747 if (payload[i].source === 'IPython.zmq.page.page') {
747 if (payload[i].source === 'IPython.zmq.page.page') {
748 if (payload[i].text.trim() !== '') {
748 if (payload[i].text.trim() !== '') {
749 IPython.pager.clear();
749 IPython.pager.clear();
750 IPython.pager.expand();
750 IPython.pager.expand();
751 IPython.pager.append_text(payload[i].text);
751 IPython.pager.append_text(payload[i].text);
752 }
752 }
753 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
753 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
754 var index = this.find_cell_index(cell);
754 var index = this.find_cell_index(cell);
755 var new_cell = this.insert_code_cell_below(index);
755 var new_cell = this.insert_code_cell_below(index);
756 new_cell.set_code(payload[i].text);
756 new_cell.set_code(payload[i].text);
757 this.dirty = true;
757 this.dirty = true;
758 }
758 }
759 };
759 };
760 };
760 };
761
761
762
762
763 Notebook.prototype.handle_iopub_reply = function (e) {
763 Notebook.prototype.handle_iopub_reply = function (e) {
764 reply = $.parseJSON(e.data);
764 reply = $.parseJSON(e.data);
765 var content = reply.content;
765 var content = reply.content;
766 // console.log(reply);
766 // console.log(reply);
767 var msg_type = reply.header.msg_type;
767 var msg_type = reply.header.msg_type;
768 var cell = this.cell_for_msg(reply.parent_header.msg_id);
768 var cell = this.cell_for_msg(reply.parent_header.msg_id);
769 if (msg_type !== 'status' && !cell){
769 if (msg_type !== 'status' && !cell){
770 // message not from this notebook, but should be attached to a cell
770 // message not from this notebook, but should be attached to a cell
771 console.log("Received IOPub message not caused by one of my cells");
771 console.log("Received IOPub message not caused by one of my cells");
772 console.log(reply);
772 console.log(reply);
773 return;
773 return;
774 }
774 }
775 var output_types = ['stream','display_data','pyout','pyerr'];
775 var output_types = ['stream','display_data','pyout','pyerr'];
776 if (output_types.indexOf(msg_type) >= 0) {
776 if (output_types.indexOf(msg_type) >= 0) {
777 this.handle_output(cell, msg_type, content);
777 this.handle_output(cell, msg_type, content);
778 } else if (msg_type === 'status') {
778 } else if (msg_type === 'status') {
779 if (content.execution_state === 'busy') {
779 if (content.execution_state === 'busy') {
780 IPython.kernel_status_widget.status_busy();
780 IPython.kernel_status_widget.status_busy();
781 } else if (content.execution_state === 'idle') {
781 } else if (content.execution_state === 'idle') {
782 IPython.kernel_status_widget.status_idle();
782 IPython.kernel_status_widget.status_idle();
783 } else if (content.execution_state === 'dead') {
783 } else if (content.execution_state === 'dead') {
784 this.handle_status_dead();
784 this.handle_status_dead();
785 };
785 };
786 } else if (msg_type === 'clear_output') {
786 } else if (msg_type === 'clear_output') {
787 cell.clear_output(content.stdout, content.stderr, content.other);
787 cell.clear_output(content.stdout, content.stderr, content.other);
788 };
788 };
789 };
789 };
790
790
791
791
792 Notebook.prototype.handle_status_dead = function () {
792 Notebook.prototype.handle_status_dead = function () {
793 var that = this;
793 var that = this;
794 this.kernel.stop_channels();
794 this.kernel.stop_channels();
795 var dialog = $('<div/>');
795 var dialog = $('<div/>');
796 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.');
796 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.');
797 $(document).append(dialog);
797 $(document).append(dialog);
798 dialog.dialog({
798 dialog.dialog({
799 resizable: false,
799 resizable: false,
800 modal: true,
800 modal: true,
801 title: "Dead kernel",
801 title: "Dead kernel",
802 buttons : {
802 buttons : {
803 "Restart": function () {
803 "Restart": function () {
804 that.start_kernel();
804 that.start_kernel();
805 $(this).dialog('close');
805 $(this).dialog('close');
806 },
806 },
807 "Continue running": function () {
807 "Continue running": function () {
808 $(this).dialog('close');
808 $(this).dialog('close');
809 }
809 }
810 }
810 }
811 });
811 });
812 };
812 };
813
813
814
814
815 Notebook.prototype.handle_output = function (cell, msg_type, content) {
815 Notebook.prototype.handle_output = function (cell, msg_type, content) {
816 var json = {};
816 var json = {};
817 json.output_type = msg_type;
817 json.output_type = msg_type;
818 if (msg_type === "stream") {
818 if (msg_type === "stream") {
819 json.text = utils.fixConsole(content.data);
819 json.text = content.data;
820 json.stream = content.name;
820 json.stream = content.name;
821 } else if (msg_type === "display_data") {
821 } else if (msg_type === "display_data") {
822 json = this.convert_mime_types(json, content.data);
822 json = this.convert_mime_types(json, content.data);
823 } else if (msg_type === "pyout") {
823 } else if (msg_type === "pyout") {
824 json.prompt_number = content.execution_count;
824 json.prompt_number = content.execution_count;
825 json = this.convert_mime_types(json, content.data);
825 json = this.convert_mime_types(json, content.data);
826 } else if (msg_type === "pyerr") {
826 } else if (msg_type === "pyerr") {
827 json.ename = content.ename;
827 json.ename = content.ename;
828 json.evalue = content.evalue;
828 json.evalue = content.evalue;
829 var traceback = [];
829 json.traceback = content.traceback;
830 for (var i=0; i<content.traceback.length; i++) {
831 traceback.push(utils.fixConsole(content.traceback[i]));
832 }
833 json.traceback = traceback;
834 };
830 };
835 cell.append_output(json);
831 cell.append_output(json);
836 this.dirty = true;
832 this.dirty = true;
837 };
833 };
838
834
839
835
840 Notebook.prototype.convert_mime_types = function (json, data) {
836 Notebook.prototype.convert_mime_types = function (json, data) {
841 if (data['text/plain'] !== undefined) {
837 if (data['text/plain'] !== undefined) {
842 json.text = utils.fixConsole(data['text/plain']);
838 json.text = data['text/plain'];
843 };
839 };
844 if (data['text/html'] !== undefined) {
840 if (data['text/html'] !== undefined) {
845 json.html = data['text/html'];
841 json.html = data['text/html'];
846 };
842 };
847 if (data['image/svg+xml'] !== undefined) {
843 if (data['image/svg+xml'] !== undefined) {
848 json.svg = data['image/svg+xml'];
844 json.svg = data['image/svg+xml'];
849 };
845 };
850 if (data['image/png'] !== undefined) {
846 if (data['image/png'] !== undefined) {
851 json.png = data['image/png'];
847 json.png = data['image/png'];
852 };
848 };
853 if (data['image/jpeg'] !== undefined) {
849 if (data['image/jpeg'] !== undefined) {
854 json.jpeg = data['image/jpeg'];
850 json.jpeg = data['image/jpeg'];
855 };
851 };
856 if (data['text/latex'] !== undefined) {
852 if (data['text/latex'] !== undefined) {
857 json.latex = data['text/latex'];
853 json.latex = data['text/latex'];
858 };
854 };
859 if (data['application/json'] !== undefined) {
855 if (data['application/json'] !== undefined) {
860 json.json = data['application/json'];
856 json.json = data['application/json'];
861 };
857 };
862 if (data['application/javascript'] !== undefined) {
858 if (data['application/javascript'] !== undefined) {
863 json.javascript = data['application/javascript'];
859 json.javascript = data['application/javascript'];
864 }
860 }
865 return json;
861 return json;
866 };
862 };
867
863
868
864
869 Notebook.prototype.execute_selected_cell = function (options) {
865 Notebook.prototype.execute_selected_cell = function (options) {
870 // add_new: should a new cell be added if we are at the end of the nb
866 // add_new: should a new cell be added if we are at the end of the nb
871 // terminal: execute in terminal mode, which stays in the current cell
867 // terminal: execute in terminal mode, which stays in the current cell
872 default_options = {terminal: false, add_new: true};
868 default_options = {terminal: false, add_new: true};
873 $.extend(default_options, options);
869 $.extend(default_options, options);
874 var that = this;
870 var that = this;
875 var cell = that.selected_cell();
871 var cell = that.selected_cell();
876 var cell_index = that.find_cell_index(cell);
872 var cell_index = that.find_cell_index(cell);
877 if (cell instanceof IPython.CodeCell) {
873 if (cell instanceof IPython.CodeCell) {
878 cell.clear_output(true, true, true);
874 cell.clear_output(true, true, true);
879 cell.set_input_prompt('*');
875 cell.set_input_prompt('*');
880 cell.element.addClass("running");
876 cell.element.addClass("running");
881 var code = cell.get_code();
877 var code = cell.get_code();
882 var msg_id = that.kernel.execute(cell.get_code());
878 var msg_id = that.kernel.execute(cell.get_code());
883 that.msg_cell_map[msg_id] = cell.cell_id;
879 that.msg_cell_map[msg_id] = cell.cell_id;
884 } else if (cell instanceof IPython.HTMLCell) {
880 } else if (cell instanceof IPython.HTMLCell) {
885 cell.render();
881 cell.render();
886 }
882 }
887 if (default_options.terminal) {
883 if (default_options.terminal) {
888 cell.select_all();
884 cell.select_all();
889 } else {
885 } else {
890 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
886 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
891 that.insert_code_cell_below();
887 that.insert_code_cell_below();
892 // If we are adding a new cell at the end, scroll down to show it.
888 // If we are adding a new cell at the end, scroll down to show it.
893 that.scroll_to_bottom();
889 that.scroll_to_bottom();
894 } else {
890 } else {
895 that.select(cell_index+1);
891 that.select(cell_index+1);
896 };
892 };
897 };
893 };
898 this.dirty = true;
894 this.dirty = true;
899 };
895 };
900
896
901
897
902 Notebook.prototype.execute_all_cells = function () {
898 Notebook.prototype.execute_all_cells = function () {
903 var ncells = this.ncells();
899 var ncells = this.ncells();
904 for (var i=0; i<ncells; i++) {
900 for (var i=0; i<ncells; i++) {
905 this.select(i);
901 this.select(i);
906 this.execute_selected_cell({add_new:false});
902 this.execute_selected_cell({add_new:false});
907 };
903 };
908 this.scroll_to_bottom();
904 this.scroll_to_bottom();
909 };
905 };
910
906
911
907
912 Notebook.prototype.request_tool_tip = function (cell,func) {
908 Notebook.prototype.request_tool_tip = function (cell,func) {
913 // Feel free to shorten this logic if you are better
909 // Feel free to shorten this logic if you are better
914 // than me in regEx
910 // than me in regEx
915 // basicaly you shoul be able to get xxx.xxx.xxx from
911 // basicaly you shoul be able to get xxx.xxx.xxx from
916 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
912 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
917 // remove everything between matchin bracket (need to iterate)
913 // remove everything between matchin bracket (need to iterate)
918 matchBracket = /\([^\(\)]+\)/g;
914 matchBracket = /\([^\(\)]+\)/g;
919 oldfunc = func;
915 oldfunc = func;
920 func = func.replace(matchBracket,"");
916 func = func.replace(matchBracket,"");
921 while( oldfunc != func )
917 while( oldfunc != func )
922 {
918 {
923 oldfunc = func;
919 oldfunc = func;
924 func = func.replace(matchBracket,"");
920 func = func.replace(matchBracket,"");
925 }
921 }
926 // remove everythin after last open bracket
922 // remove everythin after last open bracket
927 endBracket = /\([^\(]*$/g;
923 endBracket = /\([^\(]*$/g;
928 func = func.replace(endBracket,"");
924 func = func.replace(endBracket,"");
929 var re = /[a-zA-Z._]+$/g;
925 var re = /[a-zA-Z._]+$/g;
930 var msg_id = this.kernel.object_info_request(re.exec(func));
926 var msg_id = this.kernel.object_info_request(re.exec(func));
931 if(typeof(msg_id)!='undefined'){
927 if(typeof(msg_id)!='undefined'){
932 this.msg_cell_map[msg_id] = cell.cell_id;
928 this.msg_cell_map[msg_id] = cell.cell_id;
933 }
929 }
934 };
930 };
935
931
936 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
932 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
937 var msg_id = this.kernel.complete(line, cursor_pos);
933 var msg_id = this.kernel.complete(line, cursor_pos);
938 this.msg_cell_map[msg_id] = cell.cell_id;
934 this.msg_cell_map[msg_id] = cell.cell_id;
939 };
935 };
940
936
941 // Persistance and loading
937 // Persistance and loading
942
938
943
939
944 Notebook.prototype.fromJSON = function (data) {
940 Notebook.prototype.fromJSON = function (data) {
945 var ncells = this.ncells();
941 var ncells = this.ncells();
946 var i;
942 var i;
947 for (i=0; i<ncells; i++) {
943 for (i=0; i<ncells; i++) {
948 // Always delete cell 0 as they get renumbered as they are deleted.
944 // Always delete cell 0 as they get renumbered as they are deleted.
949 this.delete_cell(0);
945 this.delete_cell(0);
950 };
946 };
951 // Save the metadata
947 // Save the metadata
952 this.metadata = data.metadata;
948 this.metadata = data.metadata;
953 // Only handle 1 worksheet for now.
949 // Only handle 1 worksheet for now.
954 var worksheet = data.worksheets[0];
950 var worksheet = data.worksheets[0];
955 if (worksheet !== undefined) {
951 if (worksheet !== undefined) {
956 var new_cells = worksheet.cells;
952 var new_cells = worksheet.cells;
957 ncells = new_cells.length;
953 ncells = new_cells.length;
958 var cell_data = null;
954 var cell_data = null;
959 var new_cell = null;
955 var new_cell = null;
960 for (i=0; i<ncells; i++) {
956 for (i=0; i<ncells; i++) {
961 cell_data = new_cells[i];
957 cell_data = new_cells[i];
962 if (cell_data.cell_type == 'code') {
958 if (cell_data.cell_type == 'code') {
963 new_cell = this.insert_code_cell_below();
959 new_cell = this.insert_code_cell_below();
964 new_cell.fromJSON(cell_data);
960 new_cell.fromJSON(cell_data);
965 } else if (cell_data.cell_type === 'html') {
961 } else if (cell_data.cell_type === 'html') {
966 new_cell = this.insert_html_cell_below();
962 new_cell = this.insert_html_cell_below();
967 new_cell.fromJSON(cell_data);
963 new_cell.fromJSON(cell_data);
968 } else if (cell_data.cell_type === 'markdown') {
964 } else if (cell_data.cell_type === 'markdown') {
969 new_cell = this.insert_markdown_cell_below();
965 new_cell = this.insert_markdown_cell_below();
970 new_cell.fromJSON(cell_data);
966 new_cell.fromJSON(cell_data);
971 };
967 };
972 };
968 };
973 };
969 };
974 };
970 };
975
971
976
972
977 Notebook.prototype.toJSON = function () {
973 Notebook.prototype.toJSON = function () {
978 var cells = this.cells();
974 var cells = this.cells();
979 var ncells = cells.length;
975 var ncells = cells.length;
980 cell_array = new Array(ncells);
976 cell_array = new Array(ncells);
981 for (var i=0; i<ncells; i++) {
977 for (var i=0; i<ncells; i++) {
982 cell_array[i] = cells[i].toJSON();
978 cell_array[i] = cells[i].toJSON();
983 };
979 };
984 data = {
980 data = {
985 // Only handle 1 worksheet for now.
981 // Only handle 1 worksheet for now.
986 worksheets : [{cells:cell_array}],
982 worksheets : [{cells:cell_array}],
987 metadata : this.metadata
983 metadata : this.metadata
988 };
984 };
989 return data;
985 return data;
990 };
986 };
991
987
992 Notebook.prototype.save_notebook = function () {
988 Notebook.prototype.save_notebook = function () {
993 if (IPython.save_widget.test_notebook_name()) {
989 if (IPython.save_widget.test_notebook_name()) {
994 var notebook_id = IPython.save_widget.get_notebook_id();
990 var notebook_id = IPython.save_widget.get_notebook_id();
995 var nbname = IPython.save_widget.get_notebook_name();
991 var nbname = IPython.save_widget.get_notebook_name();
996 // We may want to move the name/id/nbformat logic inside toJSON?
992 // We may want to move the name/id/nbformat logic inside toJSON?
997 var data = this.toJSON();
993 var data = this.toJSON();
998 data.metadata.name = nbname;
994 data.metadata.name = nbname;
999 data.nbformat = 2;
995 data.nbformat = 2;
1000 // We do the call with settings so we can set cache to false.
996 // We do the call with settings so we can set cache to false.
1001 var settings = {
997 var settings = {
1002 processData : false,
998 processData : false,
1003 cache : false,
999 cache : false,
1004 type : "PUT",
1000 type : "PUT",
1005 data : JSON.stringify(data),
1001 data : JSON.stringify(data),
1006 headers : {'Content-Type': 'application/json'},
1002 headers : {'Content-Type': 'application/json'},
1007 success : $.proxy(this.notebook_saved,this),
1003 success : $.proxy(this.notebook_saved,this),
1008 error : $.proxy(this.notebook_save_failed,this)
1004 error : $.proxy(this.notebook_save_failed,this)
1009 };
1005 };
1010 IPython.save_widget.status_saving();
1006 IPython.save_widget.status_saving();
1011 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1007 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1012 $.ajax(url, settings);
1008 $.ajax(url, settings);
1013 };
1009 };
1014 };
1010 };
1015
1011
1016
1012
1017 Notebook.prototype.notebook_saved = function (data, status, xhr) {
1013 Notebook.prototype.notebook_saved = function (data, status, xhr) {
1018 this.dirty = false;
1014 this.dirty = false;
1019 IPython.save_widget.notebook_saved();
1015 IPython.save_widget.notebook_saved();
1020 IPython.save_widget.status_save();
1016 IPython.save_widget.status_save();
1021 };
1017 };
1022
1018
1023
1019
1024 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
1020 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
1025 // Notify the user and reset the save button
1021 // Notify the user and reset the save button
1026 // TODO: Handle different types of errors (timeout etc.)
1022 // TODO: Handle different types of errors (timeout etc.)
1027 alert('An unexpected error occured while saving the notebook.');
1023 alert('An unexpected error occured while saving the notebook.');
1028 IPython.save_widget.reset_status();
1024 IPython.save_widget.reset_status();
1029 };
1025 };
1030
1026
1031
1027
1032 Notebook.prototype.load_notebook = function (callback) {
1028 Notebook.prototype.load_notebook = function (callback) {
1033 var that = this;
1029 var that = this;
1034 var notebook_id = IPython.save_widget.get_notebook_id();
1030 var notebook_id = IPython.save_widget.get_notebook_id();
1035 // We do the call with settings so we can set cache to false.
1031 // We do the call with settings so we can set cache to false.
1036 var settings = {
1032 var settings = {
1037 processData : false,
1033 processData : false,
1038 cache : false,
1034 cache : false,
1039 type : "GET",
1035 type : "GET",
1040 dataType : "json",
1036 dataType : "json",
1041 success : function (data, status, xhr) {
1037 success : function (data, status, xhr) {
1042 that.notebook_loaded(data, status, xhr);
1038 that.notebook_loaded(data, status, xhr);
1043 if (callback !== undefined) {
1039 if (callback !== undefined) {
1044 callback();
1040 callback();
1045 };
1041 };
1046 }
1042 }
1047 };
1043 };
1048 IPython.save_widget.status_loading();
1044 IPython.save_widget.status_loading();
1049 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1045 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1050 $.ajax(url, settings);
1046 $.ajax(url, settings);
1051 };
1047 };
1052
1048
1053
1049
1054 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
1050 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
1055 var allowed = xhr.getResponseHeader('Allow');
1051 var allowed = xhr.getResponseHeader('Allow');
1056 this.fromJSON(data);
1052 this.fromJSON(data);
1057 if (this.ncells() === 0) {
1053 if (this.ncells() === 0) {
1058 this.insert_code_cell_below();
1054 this.insert_code_cell_below();
1059 };
1055 };
1060 IPython.save_widget.status_save();
1056 IPython.save_widget.status_save();
1061 IPython.save_widget.set_notebook_name(data.metadata.name);
1057 IPython.save_widget.set_notebook_name(data.metadata.name);
1062 this.dirty = false;
1058 this.dirty = false;
1063 if (! this.read_only) {
1059 if (! this.read_only) {
1064 this.start_kernel();
1060 this.start_kernel();
1065 }
1061 }
1066 // fromJSON always selects the last cell inserted. We need to wait
1062 // fromJSON always selects the last cell inserted. We need to wait
1067 // until that is done before scrolling to the top.
1063 // until that is done before scrolling to the top.
1068 setTimeout(function () {
1064 setTimeout(function () {
1069 IPython.notebook.select(0);
1065 IPython.notebook.select(0);
1070 IPython.notebook.scroll_to_top();
1066 IPython.notebook.scroll_to_top();
1071 }, 50);
1067 }, 50);
1072 };
1068 };
1073
1069
1074 IPython.Notebook = Notebook;
1070 IPython.Notebook = Notebook;
1075
1071
1076
1072
1077 return IPython;
1073 return IPython;
1078
1074
1079 }(IPython));
1075 }(IPython));
1080
1076
General Comments 0
You need to be logged in to leave comments. Login now