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