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