##// END OF EJS Templates
Merge pull request #2090 from v923z/master...
Bussonnier Matthias -
r7926:3a57667f merge
parent child Browse files
Show More
@@ -1,1287 +1,1295 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 var Notebook = function (selector) {
17 var Notebook = function (selector) {
18 this.read_only = IPython.read_only;
18 this.read_only = IPython.read_only;
19 this.element = $(selector);
19 this.element = $(selector);
20 this.element.scroll();
20 this.element.scroll();
21 this.element.data("notebook", this);
21 this.element.data("notebook", this);
22 this.next_prompt_number = 1;
22 this.next_prompt_number = 1;
23 this.kernel = null;
23 this.kernel = null;
24 this.clipboard = null;
24 this.clipboard = null;
25 this.paste_enabled = false;
25 this.paste_enabled = false;
26 this.dirty = false;
26 this.dirty = false;
27 this.metadata = {};
27 this.metadata = {};
28 // single worksheet for now
28 // single worksheet for now
29 this.worksheet_metadata = {};
29 this.worksheet_metadata = {};
30 this.control_key_active = false;
30 this.control_key_active = false;
31 this.notebook_id = null;
31 this.notebook_id = null;
32 this.notebook_name = null;
32 this.notebook_name = null;
33 this.notebook_name_blacklist_re = /[\/\\:]/;
33 this.notebook_name_blacklist_re = /[\/\\:]/;
34 this.nbformat = 3 // Increment this when changing the nbformat
34 this.nbformat = 3 // Increment this when changing the nbformat
35 this.nbformat_minor = 0 // Increment this when changing the nbformat
35 this.nbformat_minor = 0 // Increment this when changing the nbformat
36 this.style();
36 this.style();
37 this.create_elements();
37 this.create_elements();
38 this.bind_events();
38 this.bind_events();
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
66
67 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
67 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
68 var index = that.find_cell_index(data.cell);
68 var index = that.find_cell_index(data.cell);
69 var new_cell = that.insert_cell_below('code',index);
69 var new_cell = that.insert_cell_below('code',index);
70 new_cell.set_text(data.text);
70 new_cell.set_text(data.text);
71 that.dirty = true;
71 that.dirty = true;
72 });
72 });
73
73
74 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
74 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
75 that.dirty = data.value;
75 that.dirty = data.value;
76 });
76 });
77
77
78 $([IPython.events]).on('select.Cell', function (event, data) {
78 $([IPython.events]).on('select.Cell', function (event, data) {
79 var index = that.find_cell_index(data.cell);
79 var index = that.find_cell_index(data.cell);
80 that.select(index);
80 that.select(index);
81 });
81 });
82
82
83
83
84 $(document).keydown(function (event) {
84 $(document).keydown(function (event) {
85 // console.log(event);
85 // console.log(event);
86 if (that.read_only) return true;
86 if (that.read_only) return true;
87
87
88 // Save (CTRL+S) or (AppleKey+S)
88 // Save (CTRL+S) or (AppleKey+S)
89 //metaKey = applekey on mac
89 //metaKey = applekey on mac
90 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
90 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
91 that.save_notebook();
91 that.save_notebook();
92 event.preventDefault();
92 event.preventDefault();
93 return false;
93 return false;
94 } else if (event.which === key.ESC) {
94 } else if (event.which === key.ESC) {
95 // Intercept escape at highest level to avoid closing
95 // Intercept escape at highest level to avoid closing
96 // websocket connection with firefox
96 // websocket connection with firefox
97 event.preventDefault();
97 event.preventDefault();
98 } else if (event.which === key.SHIFT) {
98 } else if (event.which === key.SHIFT) {
99 // ignore shift keydown
99 // ignore shift keydown
100 return true;
100 return true;
101 }
101 }
102 if (event.which === key.UPARROW && !event.shiftKey) {
102 if (event.which === key.UPARROW && !event.shiftKey) {
103 var cell = that.get_selected_cell();
103 var cell = that.get_selected_cell();
104 if (cell.at_top()) {
104 if (cell.at_top()) {
105 event.preventDefault();
105 event.preventDefault();
106 that.select_prev();
106 that.select_prev();
107 };
107 };
108 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
108 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
109 var cell = that.get_selected_cell();
109 var cell = that.get_selected_cell();
110 if (cell.at_bottom()) {
110 if (cell.at_bottom()) {
111 event.preventDefault();
111 event.preventDefault();
112 that.select_next();
112 that.select_next();
113 };
113 };
114 } else if (event.which === key.ENTER && event.shiftKey) {
114 } else if (event.which === key.ENTER && event.shiftKey) {
115 that.execute_selected_cell();
115 that.execute_selected_cell();
116 return false;
116 return false;
117 } else if (event.which === key.ENTER && event.altKey) {
118 // Execute code cell, and insert new in place
119 that.execute_selected_cell();
120 // Only insert a new cell, if we ended up in an already populated cell
121 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
122 that.insert_cell_above('code');
123 }
124 return false;
117 } else if (event.which === key.ENTER && event.ctrlKey) {
125 } else if (event.which === key.ENTER && event.ctrlKey) {
118 that.execute_selected_cell({terminal:true});
126 that.execute_selected_cell({terminal:true});
119 return false;
127 return false;
120 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
128 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
121 that.control_key_active = true;
129 that.control_key_active = true;
122 return false;
130 return false;
123 } else if (event.which === 88 && that.control_key_active) {
131 } else if (event.which === 88 && that.control_key_active) {
124 // Cut selected cell = x
132 // Cut selected cell = x
125 that.cut_cell();
133 that.cut_cell();
126 that.control_key_active = false;
134 that.control_key_active = false;
127 return false;
135 return false;
128 } else if (event.which === 67 && that.control_key_active) {
136 } else if (event.which === 67 && that.control_key_active) {
129 // Copy selected cell = c
137 // Copy selected cell = c
130 that.copy_cell();
138 that.copy_cell();
131 that.control_key_active = false;
139 that.control_key_active = false;
132 return false;
140 return false;
133 } else if (event.which === 86 && that.control_key_active) {
141 } else if (event.which === 86 && that.control_key_active) {
134 // Paste selected cell = v
142 // Paste selected cell = v
135 that.paste_cell();
143 that.paste_cell();
136 that.control_key_active = false;
144 that.control_key_active = false;
137 return false;
145 return false;
138 } else if (event.which === 68 && that.control_key_active) {
146 } else if (event.which === 68 && that.control_key_active) {
139 // Delete selected cell = d
147 // Delete selected cell = d
140 that.delete_cell();
148 that.delete_cell();
141 that.control_key_active = false;
149 that.control_key_active = false;
142 return false;
150 return false;
143 } else if (event.which === 65 && that.control_key_active) {
151 } else if (event.which === 65 && that.control_key_active) {
144 // Insert code cell above selected = a
152 // Insert code cell above selected = a
145 that.insert_cell_above('code');
153 that.insert_cell_above('code');
146 that.control_key_active = false;
154 that.control_key_active = false;
147 return false;
155 return false;
148 } else if (event.which === 66 && that.control_key_active) {
156 } else if (event.which === 66 && that.control_key_active) {
149 // Insert code cell below selected = b
157 // Insert code cell below selected = b
150 that.insert_cell_below('code');
158 that.insert_cell_below('code');
151 that.control_key_active = false;
159 that.control_key_active = false;
152 return false;
160 return false;
153 } else if (event.which === 89 && that.control_key_active) {
161 } else if (event.which === 89 && that.control_key_active) {
154 // To code = y
162 // To code = y
155 that.to_code();
163 that.to_code();
156 that.control_key_active = false;
164 that.control_key_active = false;
157 return false;
165 return false;
158 } else if (event.which === 77 && that.control_key_active) {
166 } else if (event.which === 77 && that.control_key_active) {
159 // To markdown = m
167 // To markdown = m
160 that.to_markdown();
168 that.to_markdown();
161 that.control_key_active = false;
169 that.control_key_active = false;
162 return false;
170 return false;
163 } else if (event.which === 84 && that.control_key_active) {
171 } else if (event.which === 84 && that.control_key_active) {
164 // To Raw = t
172 // To Raw = t
165 that.to_raw();
173 that.to_raw();
166 that.control_key_active = false;
174 that.control_key_active = false;
167 return false;
175 return false;
168 } else if (event.which === 49 && that.control_key_active) {
176 } else if (event.which === 49 && that.control_key_active) {
169 // To Heading 1 = 1
177 // To Heading 1 = 1
170 that.to_heading(undefined, 1);
178 that.to_heading(undefined, 1);
171 that.control_key_active = false;
179 that.control_key_active = false;
172 return false;
180 return false;
173 } else if (event.which === 50 && that.control_key_active) {
181 } else if (event.which === 50 && that.control_key_active) {
174 // To Heading 2 = 2
182 // To Heading 2 = 2
175 that.to_heading(undefined, 2);
183 that.to_heading(undefined, 2);
176 that.control_key_active = false;
184 that.control_key_active = false;
177 return false;
185 return false;
178 } else if (event.which === 51 && that.control_key_active) {
186 } else if (event.which === 51 && that.control_key_active) {
179 // To Heading 3 = 3
187 // To Heading 3 = 3
180 that.to_heading(undefined, 3);
188 that.to_heading(undefined, 3);
181 that.control_key_active = false;
189 that.control_key_active = false;
182 return false;
190 return false;
183 } else if (event.which === 52 && that.control_key_active) {
191 } else if (event.which === 52 && that.control_key_active) {
184 // To Heading 4 = 4
192 // To Heading 4 = 4
185 that.to_heading(undefined, 4);
193 that.to_heading(undefined, 4);
186 that.control_key_active = false;
194 that.control_key_active = false;
187 return false;
195 return false;
188 } else if (event.which === 53 && that.control_key_active) {
196 } else if (event.which === 53 && that.control_key_active) {
189 // To Heading 5 = 5
197 // To Heading 5 = 5
190 that.to_heading(undefined, 5);
198 that.to_heading(undefined, 5);
191 that.control_key_active = false;
199 that.control_key_active = false;
192 return false;
200 return false;
193 } else if (event.which === 54 && that.control_key_active) {
201 } else if (event.which === 54 && that.control_key_active) {
194 // To Heading 6 = 6
202 // To Heading 6 = 6
195 that.to_heading(undefined, 6);
203 that.to_heading(undefined, 6);
196 that.control_key_active = false;
204 that.control_key_active = false;
197 return false;
205 return false;
198 } else if (event.which === 79 && that.control_key_active) {
206 } else if (event.which === 79 && that.control_key_active) {
199 // Toggle output = o
207 // Toggle output = o
200 if (event.shiftKey){
208 if (event.shiftKey){
201 that.toggle_output_scroll();
209 that.toggle_output_scroll();
202 } else {
210 } else {
203 that.toggle_output();
211 that.toggle_output();
204 }
212 }
205 that.control_key_active = false;
213 that.control_key_active = false;
206 return false;
214 return false;
207 } else if (event.which === 83 && that.control_key_active) {
215 } else if (event.which === 83 && that.control_key_active) {
208 // Save notebook = s
216 // Save notebook = s
209 that.save_notebook();
217 that.save_notebook();
210 that.control_key_active = false;
218 that.control_key_active = false;
211 return false;
219 return false;
212 } else if (event.which === 74 && that.control_key_active) {
220 } else if (event.which === 74 && that.control_key_active) {
213 // Move cell down = j
221 // Move cell down = j
214 that.move_cell_down();
222 that.move_cell_down();
215 that.control_key_active = false;
223 that.control_key_active = false;
216 return false;
224 return false;
217 } else if (event.which === 75 && that.control_key_active) {
225 } else if (event.which === 75 && that.control_key_active) {
218 // Move cell up = k
226 // Move cell up = k
219 that.move_cell_up();
227 that.move_cell_up();
220 that.control_key_active = false;
228 that.control_key_active = false;
221 return false;
229 return false;
222 } else if (event.which === 80 && that.control_key_active) {
230 } else if (event.which === 80 && that.control_key_active) {
223 // Select previous = p
231 // Select previous = p
224 that.select_prev();
232 that.select_prev();
225 that.control_key_active = false;
233 that.control_key_active = false;
226 return false;
234 return false;
227 } else if (event.which === 78 && that.control_key_active) {
235 } else if (event.which === 78 && that.control_key_active) {
228 // Select next = n
236 // Select next = n
229 that.select_next();
237 that.select_next();
230 that.control_key_active = false;
238 that.control_key_active = false;
231 return false;
239 return false;
232 } else if (event.which === 76 && that.control_key_active) {
240 } else if (event.which === 76 && that.control_key_active) {
233 // Toggle line numbers = l
241 // Toggle line numbers = l
234 that.cell_toggle_line_numbers();
242 that.cell_toggle_line_numbers();
235 that.control_key_active = false;
243 that.control_key_active = false;
236 return false;
244 return false;
237 } else if (event.which === 73 && that.control_key_active) {
245 } else if (event.which === 73 && that.control_key_active) {
238 // Interrupt kernel = i
246 // Interrupt kernel = i
239 that.kernel.interrupt();
247 that.kernel.interrupt();
240 that.control_key_active = false;
248 that.control_key_active = false;
241 return false;
249 return false;
242 } else if (event.which === 190 && that.control_key_active) {
250 } else if (event.which === 190 && that.control_key_active) {
243 // Restart kernel = . # matches qt console
251 // Restart kernel = . # matches qt console
244 that.restart_kernel();
252 that.restart_kernel();
245 that.control_key_active = false;
253 that.control_key_active = false;
246 return false;
254 return false;
247 } else if (event.which === 72 && that.control_key_active) {
255 } else if (event.which === 72 && that.control_key_active) {
248 // Show keyboard shortcuts = h
256 // Show keyboard shortcuts = h
249 IPython.quick_help.show_keyboard_shortcuts();
257 IPython.quick_help.show_keyboard_shortcuts();
250 that.control_key_active = false;
258 that.control_key_active = false;
251 return false;
259 return false;
252 } else if (that.control_key_active) {
260 } else if (that.control_key_active) {
253 that.control_key_active = false;
261 that.control_key_active = false;
254 return true;
262 return true;
255 };
263 };
256 return true;
264 return true;
257 });
265 });
258
266
259 var collapse_time = function(time){
267 var collapse_time = function(time){
260 var app_height = $('div#main_app').height(); // content height
268 var app_height = $('div#main_app').height(); // content height
261 var splitter_height = $('div#pager_splitter').outerHeight(true);
269 var splitter_height = $('div#pager_splitter').outerHeight(true);
262 var new_height = app_height - splitter_height;
270 var new_height = app_height - splitter_height;
263 that.element.animate({height : new_height + 'px'}, time);
271 that.element.animate({height : new_height + 'px'}, time);
264 }
272 }
265
273
266 this.element.bind('collapse_pager', function (event,extrap) {
274 this.element.bind('collapse_pager', function (event,extrap) {
267 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
275 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
268 collapse_time(time);
276 collapse_time(time);
269 });
277 });
270
278
271 var expand_time = function(time) {
279 var expand_time = function(time) {
272 var app_height = $('div#main_app').height(); // content height
280 var app_height = $('div#main_app').height(); // content height
273 var splitter_height = $('div#pager_splitter').outerHeight(true);
281 var splitter_height = $('div#pager_splitter').outerHeight(true);
274 var pager_height = $('div#pager').outerHeight(true);
282 var pager_height = $('div#pager').outerHeight(true);
275 var new_height = app_height - pager_height - splitter_height;
283 var new_height = app_height - pager_height - splitter_height;
276 that.element.animate({height : new_height + 'px'}, time);
284 that.element.animate({height : new_height + 'px'}, time);
277 }
285 }
278
286
279 this.element.bind('expand_pager', function (event, extrap) {
287 this.element.bind('expand_pager', function (event, extrap) {
280 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
288 time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
281 expand_time(time);
289 expand_time(time);
282 });
290 });
283
291
284 $(window).bind('beforeunload', function () {
292 $(window).bind('beforeunload', function () {
285 // TODO: Make killing the kernel configurable.
293 // TODO: Make killing the kernel configurable.
286 var kill_kernel = false;
294 var kill_kernel = false;
287 if (kill_kernel) {
295 if (kill_kernel) {
288 that.kernel.kill();
296 that.kernel.kill();
289 }
297 }
290 if (that.dirty && ! that.read_only) {
298 if (that.dirty && ! that.read_only) {
291 return "You have unsaved changes that will be lost if you leave this page.";
299 return "You have unsaved changes that will be lost if you leave this page.";
292 };
300 };
293 // Null is the *only* return value that will make the browser not
301 // Null is the *only* return value that will make the browser not
294 // pop up the "don't leave" dialog.
302 // pop up the "don't leave" dialog.
295 return null;
303 return null;
296 });
304 });
297 };
305 };
298
306
299
307
300 Notebook.prototype.scroll_to_bottom = function () {
308 Notebook.prototype.scroll_to_bottom = function () {
301 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
309 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
302 };
310 };
303
311
304
312
305 Notebook.prototype.scroll_to_top = function () {
313 Notebook.prototype.scroll_to_top = function () {
306 this.element.animate({scrollTop:0}, 0);
314 this.element.animate({scrollTop:0}, 0);
307 };
315 };
308
316
309
317
310 // Cell indexing, retrieval, etc.
318 // Cell indexing, retrieval, etc.
311
319
312 Notebook.prototype.get_cell_elements = function () {
320 Notebook.prototype.get_cell_elements = function () {
313 return this.element.children("div.cell");
321 return this.element.children("div.cell");
314 };
322 };
315
323
316
324
317 Notebook.prototype.get_cell_element = function (index) {
325 Notebook.prototype.get_cell_element = function (index) {
318 var result = null;
326 var result = null;
319 var e = this.get_cell_elements().eq(index);
327 var e = this.get_cell_elements().eq(index);
320 if (e.length !== 0) {
328 if (e.length !== 0) {
321 result = e;
329 result = e;
322 }
330 }
323 return result;
331 return result;
324 };
332 };
325
333
326
334
327 Notebook.prototype.ncells = function (cell) {
335 Notebook.prototype.ncells = function (cell) {
328 return this.get_cell_elements().length;
336 return this.get_cell_elements().length;
329 };
337 };
330
338
331
339
332 // TODO: we are often calling cells as cells()[i], which we should optimize
340 // TODO: we are often calling cells as cells()[i], which we should optimize
333 // to cells(i) or a new method.
341 // to cells(i) or a new method.
334 Notebook.prototype.get_cells = function () {
342 Notebook.prototype.get_cells = function () {
335 return this.get_cell_elements().toArray().map(function (e) {
343 return this.get_cell_elements().toArray().map(function (e) {
336 return $(e).data("cell");
344 return $(e).data("cell");
337 });
345 });
338 };
346 };
339
347
340
348
341 Notebook.prototype.get_cell = function (index) {
349 Notebook.prototype.get_cell = function (index) {
342 var result = null;
350 var result = null;
343 var ce = this.get_cell_element(index);
351 var ce = this.get_cell_element(index);
344 if (ce !== null) {
352 if (ce !== null) {
345 result = ce.data('cell');
353 result = ce.data('cell');
346 }
354 }
347 return result;
355 return result;
348 }
356 }
349
357
350
358
351 Notebook.prototype.get_next_cell = function (cell) {
359 Notebook.prototype.get_next_cell = function (cell) {
352 var result = null;
360 var result = null;
353 var index = this.find_cell_index(cell);
361 var index = this.find_cell_index(cell);
354 if (index !== null && index < this.ncells()) {
362 if (index !== null && index < this.ncells()) {
355 result = this.get_cell(index+1);
363 result = this.get_cell(index+1);
356 }
364 }
357 return result;
365 return result;
358 }
366 }
359
367
360
368
361 Notebook.prototype.get_prev_cell = function (cell) {
369 Notebook.prototype.get_prev_cell = function (cell) {
362 var result = null;
370 var result = null;
363 var index = this.find_cell_index(cell);
371 var index = this.find_cell_index(cell);
364 if (index !== null && index > 1) {
372 if (index !== null && index > 1) {
365 result = this.get_cell(index-1);
373 result = this.get_cell(index-1);
366 }
374 }
367 return result;
375 return result;
368 }
376 }
369
377
370 Notebook.prototype.find_cell_index = function (cell) {
378 Notebook.prototype.find_cell_index = function (cell) {
371 var result = null;
379 var result = null;
372 this.get_cell_elements().filter(function (index) {
380 this.get_cell_elements().filter(function (index) {
373 if ($(this).data("cell") === cell) {
381 if ($(this).data("cell") === cell) {
374 result = index;
382 result = index;
375 };
383 };
376 });
384 });
377 return result;
385 return result;
378 };
386 };
379
387
380
388
381 Notebook.prototype.index_or_selected = function (index) {
389 Notebook.prototype.index_or_selected = function (index) {
382 var i;
390 var i;
383 if (index === undefined || index === null) {
391 if (index === undefined || index === null) {
384 i = this.get_selected_index();
392 i = this.get_selected_index();
385 if (i === null) {
393 if (i === null) {
386 i = 0;
394 i = 0;
387 }
395 }
388 } else {
396 } else {
389 i = index;
397 i = index;
390 }
398 }
391 return i;
399 return i;
392 };
400 };
393
401
394
402
395 Notebook.prototype.get_selected_cell = function () {
403 Notebook.prototype.get_selected_cell = function () {
396 var index = this.get_selected_index();
404 var index = this.get_selected_index();
397 return this.get_cell(index);
405 return this.get_cell(index);
398 };
406 };
399
407
400
408
401 Notebook.prototype.is_valid_cell_index = function (index) {
409 Notebook.prototype.is_valid_cell_index = function (index) {
402 if (index !== null && index >= 0 && index < this.ncells()) {
410 if (index !== null && index >= 0 && index < this.ncells()) {
403 return true;
411 return true;
404 } else {
412 } else {
405 return false;
413 return false;
406 };
414 };
407 }
415 }
408
416
409 Notebook.prototype.get_selected_index = function () {
417 Notebook.prototype.get_selected_index = function () {
410 var result = null;
418 var result = null;
411 this.get_cell_elements().filter(function (index) {
419 this.get_cell_elements().filter(function (index) {
412 if ($(this).data("cell").selected === true) {
420 if ($(this).data("cell").selected === true) {
413 result = index;
421 result = index;
414 };
422 };
415 });
423 });
416 return result;
424 return result;
417 };
425 };
418
426
419
427
420 // Cell selection.
428 // Cell selection.
421
429
422 Notebook.prototype.select = function (index) {
430 Notebook.prototype.select = function (index) {
423 if (index !== undefined && index >= 0 && index < this.ncells()) {
431 if (index !== undefined && index >= 0 && index < this.ncells()) {
424 sindex = this.get_selected_index()
432 sindex = this.get_selected_index()
425 if (sindex !== null && index !== sindex) {
433 if (sindex !== null && index !== sindex) {
426 this.get_cell(sindex).unselect();
434 this.get_cell(sindex).unselect();
427 };
435 };
428 var cell = this.get_cell(index)
436 var cell = this.get_cell(index)
429 cell.select();
437 cell.select();
430 if (cell.cell_type === 'heading') {
438 if (cell.cell_type === 'heading') {
431 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
439 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
432 {'cell_type':cell.cell_type,level:cell.level}
440 {'cell_type':cell.cell_type,level:cell.level}
433 );
441 );
434 } else {
442 } else {
435 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
443 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
436 {'cell_type':cell.cell_type}
444 {'cell_type':cell.cell_type}
437 );
445 );
438 };
446 };
439 };
447 };
440 return this;
448 return this;
441 };
449 };
442
450
443
451
444 Notebook.prototype.select_next = function () {
452 Notebook.prototype.select_next = function () {
445 var index = this.get_selected_index();
453 var index = this.get_selected_index();
446 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
454 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
447 this.select(index+1);
455 this.select(index+1);
448 };
456 };
449 return this;
457 return this;
450 };
458 };
451
459
452
460
453 Notebook.prototype.select_prev = function () {
461 Notebook.prototype.select_prev = function () {
454 var index = this.get_selected_index();
462 var index = this.get_selected_index();
455 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
463 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
456 this.select(index-1);
464 this.select(index-1);
457 };
465 };
458 return this;
466 return this;
459 };
467 };
460
468
461
469
462 // Cell movement
470 // Cell movement
463
471
464 Notebook.prototype.move_cell_up = function (index) {
472 Notebook.prototype.move_cell_up = function (index) {
465 var i = this.index_or_selected();
473 var i = this.index_or_selected();
466 if (i !== null && i < this.ncells() && i > 0) {
474 if (i !== null && i < this.ncells() && i > 0) {
467 var pivot = this.get_cell_element(i-1);
475 var pivot = this.get_cell_element(i-1);
468 var tomove = this.get_cell_element(i);
476 var tomove = this.get_cell_element(i);
469 if (pivot !== null && tomove !== null) {
477 if (pivot !== null && tomove !== null) {
470 tomove.detach();
478 tomove.detach();
471 pivot.before(tomove);
479 pivot.before(tomove);
472 this.select(i-1);
480 this.select(i-1);
473 };
481 };
474 };
482 };
475 this.dirty = true;
483 this.dirty = true;
476 return this;
484 return this;
477 };
485 };
478
486
479
487
480 Notebook.prototype.move_cell_down = function (index) {
488 Notebook.prototype.move_cell_down = function (index) {
481 var i = this.index_or_selected();
489 var i = this.index_or_selected();
482 if (i !== null && i < (this.ncells()-1) && i >= 0) {
490 if (i !== null && i < (this.ncells()-1) && i >= 0) {
483 var pivot = this.get_cell_element(i+1);
491 var pivot = this.get_cell_element(i+1);
484 var tomove = this.get_cell_element(i);
492 var tomove = this.get_cell_element(i);
485 if (pivot !== null && tomove !== null) {
493 if (pivot !== null && tomove !== null) {
486 tomove.detach();
494 tomove.detach();
487 pivot.after(tomove);
495 pivot.after(tomove);
488 this.select(i+1);
496 this.select(i+1);
489 };
497 };
490 };
498 };
491 this.dirty = true;
499 this.dirty = true;
492 return this;
500 return this;
493 };
501 };
494
502
495
503
496 Notebook.prototype.sort_cells = function () {
504 Notebook.prototype.sort_cells = function () {
497 // This is not working right now. Calling this will actually crash
505 // This is not working right now. Calling this will actually crash
498 // the browser. I think there is an infinite loop in here...
506 // the browser. I think there is an infinite loop in here...
499 var ncells = this.ncells();
507 var ncells = this.ncells();
500 var sindex = this.get_selected_index();
508 var sindex = this.get_selected_index();
501 var swapped;
509 var swapped;
502 do {
510 do {
503 swapped = false;
511 swapped = false;
504 for (var i=1; i<ncells; i++) {
512 for (var i=1; i<ncells; i++) {
505 current = this.get_cell(i);
513 current = this.get_cell(i);
506 previous = this.get_cell(i-1);
514 previous = this.get_cell(i-1);
507 if (previous.input_prompt_number > current.input_prompt_number) {
515 if (previous.input_prompt_number > current.input_prompt_number) {
508 this.move_cell_up(i);
516 this.move_cell_up(i);
509 swapped = true;
517 swapped = true;
510 };
518 };
511 };
519 };
512 } while (swapped);
520 } while (swapped);
513 this.select(sindex);
521 this.select(sindex);
514 return this;
522 return this;
515 };
523 };
516
524
517 // Insertion, deletion.
525 // Insertion, deletion.
518
526
519 Notebook.prototype.delete_cell = function (index) {
527 Notebook.prototype.delete_cell = function (index) {
520 var i = this.index_or_selected(index);
528 var i = this.index_or_selected(index);
521 if (this.is_valid_cell_index(i)) {
529 if (this.is_valid_cell_index(i)) {
522 var ce = this.get_cell_element(i);
530 var ce = this.get_cell_element(i);
523 ce.remove();
531 ce.remove();
524 if (i === (this.ncells())) {
532 if (i === (this.ncells())) {
525 this.select(i-1);
533 this.select(i-1);
526 } else {
534 } else {
527 this.select(i);
535 this.select(i);
528 };
536 };
529 this.dirty = true;
537 this.dirty = true;
530 };
538 };
531 return this;
539 return this;
532 };
540 };
533
541
534
542
535 Notebook.prototype.insert_cell_below = function (type, index) {
543 Notebook.prototype.insert_cell_below = function (type, index) {
536 // type = ('code','html','markdown')
544 // type = ('code','html','markdown')
537 // index = cell index or undefined to insert below selected
545 // index = cell index or undefined to insert below selected
538 index = this.index_or_selected(index);
546 index = this.index_or_selected(index);
539 var cell = null;
547 var cell = null;
540 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
548 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
541 if (type === 'code') {
549 if (type === 'code') {
542 cell = new IPython.CodeCell(this.kernel);
550 cell = new IPython.CodeCell(this.kernel);
543 cell.set_input_prompt();
551 cell.set_input_prompt();
544 } else if (type === 'markdown') {
552 } else if (type === 'markdown') {
545 cell = new IPython.MarkdownCell();
553 cell = new IPython.MarkdownCell();
546 } else if (type === 'html') {
554 } else if (type === 'html') {
547 cell = new IPython.HTMLCell();
555 cell = new IPython.HTMLCell();
548 } else if (type === 'raw') {
556 } else if (type === 'raw') {
549 cell = new IPython.RawCell();
557 cell = new IPython.RawCell();
550 } else if (type === 'heading') {
558 } else if (type === 'heading') {
551 cell = new IPython.HeadingCell();
559 cell = new IPython.HeadingCell();
552 };
560 };
553 if (cell !== null) {
561 if (cell !== null) {
554 if (this.ncells() === 0) {
562 if (this.ncells() === 0) {
555 this.element.find('div.end_space').before(cell.element);
563 this.element.find('div.end_space').before(cell.element);
556 } else if (this.is_valid_cell_index(index)) {
564 } else if (this.is_valid_cell_index(index)) {
557 this.get_cell_element(index).after(cell.element);
565 this.get_cell_element(index).after(cell.element);
558 };
566 };
559 cell.render();
567 cell.render();
560 this.select(this.find_cell_index(cell));
568 this.select(this.find_cell_index(cell));
561 this.dirty = true;
569 this.dirty = true;
562 return cell;
570 return cell;
563 };
571 };
564 };
572 };
565 return cell;
573 return cell;
566 };
574 };
567
575
568
576
569 Notebook.prototype.insert_cell_above = function (type, index) {
577 Notebook.prototype.insert_cell_above = function (type, index) {
570 // type = ('code','html','markdown')
578 // type = ('code','html','markdown')
571 // index = cell index or undefined to insert above selected
579 // index = cell index or undefined to insert above selected
572 index = this.index_or_selected(index);
580 index = this.index_or_selected(index);
573 var cell = null;
581 var cell = null;
574 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
582 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
575 if (type === 'code') {
583 if (type === 'code') {
576 cell = new IPython.CodeCell(this.kernel);
584 cell = new IPython.CodeCell(this.kernel);
577 cell.set_input_prompt();
585 cell.set_input_prompt();
578 } else if (type === 'markdown') {
586 } else if (type === 'markdown') {
579 cell = new IPython.MarkdownCell();
587 cell = new IPython.MarkdownCell();
580 } else if (type === 'html') {
588 } else if (type === 'html') {
581 cell = new IPython.HTMLCell();
589 cell = new IPython.HTMLCell();
582 } else if (type === 'raw') {
590 } else if (type === 'raw') {
583 cell = new IPython.RawCell();
591 cell = new IPython.RawCell();
584 } else if (type === 'heading') {
592 } else if (type === 'heading') {
585 cell = new IPython.HeadingCell();
593 cell = new IPython.HeadingCell();
586 };
594 };
587 if (cell !== null) {
595 if (cell !== null) {
588 if (this.ncells() === 0) {
596 if (this.ncells() === 0) {
589 this.element.find('div.end_space').before(cell.element);
597 this.element.find('div.end_space').before(cell.element);
590 } else if (this.is_valid_cell_index(index)) {
598 } else if (this.is_valid_cell_index(index)) {
591 this.get_cell_element(index).before(cell.element);
599 this.get_cell_element(index).before(cell.element);
592 };
600 };
593 cell.render();
601 cell.render();
594 this.select(this.find_cell_index(cell));
602 this.select(this.find_cell_index(cell));
595 this.dirty = true;
603 this.dirty = true;
596 return cell;
604 return cell;
597 };
605 };
598 };
606 };
599 return cell;
607 return cell;
600 };
608 };
601
609
602
610
603 Notebook.prototype.to_code = function (index) {
611 Notebook.prototype.to_code = function (index) {
604 var i = this.index_or_selected(index);
612 var i = this.index_or_selected(index);
605 if (this.is_valid_cell_index(i)) {
613 if (this.is_valid_cell_index(i)) {
606 var source_element = this.get_cell_element(i);
614 var source_element = this.get_cell_element(i);
607 var source_cell = source_element.data("cell");
615 var source_cell = source_element.data("cell");
608 if (!(source_cell instanceof IPython.CodeCell)) {
616 if (!(source_cell instanceof IPython.CodeCell)) {
609 target_cell = this.insert_cell_below('code',i);
617 target_cell = this.insert_cell_below('code',i);
610 var text = source_cell.get_text();
618 var text = source_cell.get_text();
611 if (text === source_cell.placeholder) {
619 if (text === source_cell.placeholder) {
612 text = '';
620 text = '';
613 }
621 }
614 target_cell.set_text(text);
622 target_cell.set_text(text);
615 // make this value the starting point, so that we can only undo
623 // make this value the starting point, so that we can only undo
616 // to this state, instead of a blank cell
624 // to this state, instead of a blank cell
617 target_cell.code_mirror.clearHistory();
625 target_cell.code_mirror.clearHistory();
618 source_element.remove();
626 source_element.remove();
619 this.dirty = true;
627 this.dirty = true;
620 };
628 };
621 };
629 };
622 };
630 };
623
631
624
632
625 Notebook.prototype.to_markdown = function (index) {
633 Notebook.prototype.to_markdown = function (index) {
626 var i = this.index_or_selected(index);
634 var i = this.index_or_selected(index);
627 if (this.is_valid_cell_index(i)) {
635 if (this.is_valid_cell_index(i)) {
628 var source_element = this.get_cell_element(i);
636 var source_element = this.get_cell_element(i);
629 var source_cell = source_element.data("cell");
637 var source_cell = source_element.data("cell");
630 if (!(source_cell instanceof IPython.MarkdownCell)) {
638 if (!(source_cell instanceof IPython.MarkdownCell)) {
631 target_cell = this.insert_cell_below('markdown',i);
639 target_cell = this.insert_cell_below('markdown',i);
632 var text = source_cell.get_text();
640 var text = source_cell.get_text();
633 if (text === source_cell.placeholder) {
641 if (text === source_cell.placeholder) {
634 text = '';
642 text = '';
635 };
643 };
636 // The edit must come before the set_text.
644 // The edit must come before the set_text.
637 target_cell.edit();
645 target_cell.edit();
638 target_cell.set_text(text);
646 target_cell.set_text(text);
639 // make this value the starting point, so that we can only undo
647 // make this value the starting point, so that we can only undo
640 // to this state, instead of a blank cell
648 // to this state, instead of a blank cell
641 target_cell.code_mirror.clearHistory();
649 target_cell.code_mirror.clearHistory();
642 source_element.remove();
650 source_element.remove();
643 this.dirty = true;
651 this.dirty = true;
644 };
652 };
645 };
653 };
646 };
654 };
647
655
648
656
649 Notebook.prototype.to_html = function (index) {
657 Notebook.prototype.to_html = function (index) {
650 var i = this.index_or_selected(index);
658 var i = this.index_or_selected(index);
651 if (this.is_valid_cell_index(i)) {
659 if (this.is_valid_cell_index(i)) {
652 var source_element = this.get_cell_element(i);
660 var source_element = this.get_cell_element(i);
653 var source_cell = source_element.data("cell");
661 var source_cell = source_element.data("cell");
654 var target_cell = null;
662 var target_cell = null;
655 if (!(source_cell instanceof IPython.HTMLCell)) {
663 if (!(source_cell instanceof IPython.HTMLCell)) {
656 target_cell = this.insert_cell_below('html',i);
664 target_cell = this.insert_cell_below('html',i);
657 var text = source_cell.get_text();
665 var text = source_cell.get_text();
658 if (text === source_cell.placeholder) {
666 if (text === source_cell.placeholder) {
659 text = '';
667 text = '';
660 };
668 };
661 // The edit must come before the set_text.
669 // The edit must come before the set_text.
662 target_cell.edit();
670 target_cell.edit();
663 target_cell.set_text(text);
671 target_cell.set_text(text);
664 // make this value the starting point, so that we can only undo
672 // make this value the starting point, so that we can only undo
665 // to this state, instead of a blank cell
673 // to this state, instead of a blank cell
666 target_cell.code_mirror.clearHistory();
674 target_cell.code_mirror.clearHistory();
667 source_element.remove();
675 source_element.remove();
668 this.dirty = true;
676 this.dirty = true;
669 };
677 };
670 };
678 };
671 };
679 };
672
680
673
681
674 Notebook.prototype.to_raw = function (index) {
682 Notebook.prototype.to_raw = function (index) {
675 var i = this.index_or_selected(index);
683 var i = this.index_or_selected(index);
676 if (this.is_valid_cell_index(i)) {
684 if (this.is_valid_cell_index(i)) {
677 var source_element = this.get_cell_element(i);
685 var source_element = this.get_cell_element(i);
678 var source_cell = source_element.data("cell");
686 var source_cell = source_element.data("cell");
679 var target_cell = null;
687 var target_cell = null;
680 if (!(source_cell instanceof IPython.RawCell)) {
688 if (!(source_cell instanceof IPython.RawCell)) {
681 target_cell = this.insert_cell_below('raw',i);
689 target_cell = this.insert_cell_below('raw',i);
682 var text = source_cell.get_text();
690 var text = source_cell.get_text();
683 if (text === source_cell.placeholder) {
691 if (text === source_cell.placeholder) {
684 text = '';
692 text = '';
685 };
693 };
686 // The edit must come before the set_text.
694 // The edit must come before the set_text.
687 target_cell.edit();
695 target_cell.edit();
688 target_cell.set_text(text);
696 target_cell.set_text(text);
689 // make this value the starting point, so that we can only undo
697 // make this value the starting point, so that we can only undo
690 // to this state, instead of a blank cell
698 // to this state, instead of a blank cell
691 target_cell.code_mirror.clearHistory();
699 target_cell.code_mirror.clearHistory();
692 source_element.remove();
700 source_element.remove();
693 this.dirty = true;
701 this.dirty = true;
694 };
702 };
695 };
703 };
696 };
704 };
697
705
698
706
699 Notebook.prototype.to_heading = function (index, level) {
707 Notebook.prototype.to_heading = function (index, level) {
700 level = level || 1;
708 level = level || 1;
701 var i = this.index_or_selected(index);
709 var i = this.index_or_selected(index);
702 if (this.is_valid_cell_index(i)) {
710 if (this.is_valid_cell_index(i)) {
703 var source_element = this.get_cell_element(i);
711 var source_element = this.get_cell_element(i);
704 var source_cell = source_element.data("cell");
712 var source_cell = source_element.data("cell");
705 var target_cell = null;
713 var target_cell = null;
706 if (source_cell instanceof IPython.HeadingCell) {
714 if (source_cell instanceof IPython.HeadingCell) {
707 source_cell.set_level(level);
715 source_cell.set_level(level);
708 } else {
716 } else {
709 target_cell = this.insert_cell_below('heading',i);
717 target_cell = this.insert_cell_below('heading',i);
710 var text = source_cell.get_text();
718 var text = source_cell.get_text();
711 if (text === source_cell.placeholder) {
719 if (text === source_cell.placeholder) {
712 text = '';
720 text = '';
713 };
721 };
714 // The edit must come before the set_text.
722 // The edit must come before the set_text.
715 target_cell.set_level(level);
723 target_cell.set_level(level);
716 target_cell.edit();
724 target_cell.edit();
717 target_cell.set_text(text);
725 target_cell.set_text(text);
718 // make this value the starting point, so that we can only undo
726 // make this value the starting point, so that we can only undo
719 // to this state, instead of a blank cell
727 // to this state, instead of a blank cell
720 target_cell.code_mirror.clearHistory();
728 target_cell.code_mirror.clearHistory();
721 source_element.remove();
729 source_element.remove();
722 this.dirty = true;
730 this.dirty = true;
723 };
731 };
724 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
732 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
725 {'cell_type':'heading',level:level}
733 {'cell_type':'heading',level:level}
726 );
734 );
727 };
735 };
728 };
736 };
729
737
730
738
731 // Cut/Copy/Paste
739 // Cut/Copy/Paste
732
740
733 Notebook.prototype.enable_paste = function () {
741 Notebook.prototype.enable_paste = function () {
734 var that = this;
742 var that = this;
735 if (!this.paste_enabled) {
743 if (!this.paste_enabled) {
736 $('#paste_cell').removeClass('ui-state-disabled')
744 $('#paste_cell').removeClass('ui-state-disabled')
737 .on('click', function () {that.paste_cell();});
745 .on('click', function () {that.paste_cell();});
738 $('#paste_cell_above').removeClass('ui-state-disabled')
746 $('#paste_cell_above').removeClass('ui-state-disabled')
739 .on('click', function () {that.paste_cell_above();});
747 .on('click', function () {that.paste_cell_above();});
740 $('#paste_cell_below').removeClass('ui-state-disabled')
748 $('#paste_cell_below').removeClass('ui-state-disabled')
741 .on('click', function () {that.paste_cell_below();});
749 .on('click', function () {that.paste_cell_below();});
742 this.paste_enabled = true;
750 this.paste_enabled = true;
743 };
751 };
744 };
752 };
745
753
746
754
747 Notebook.prototype.disable_paste = function () {
755 Notebook.prototype.disable_paste = function () {
748 if (this.paste_enabled) {
756 if (this.paste_enabled) {
749 $('#paste_cell').addClass('ui-state-disabled').off('click');
757 $('#paste_cell').addClass('ui-state-disabled').off('click');
750 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
758 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
751 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
759 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
752 this.paste_enabled = false;
760 this.paste_enabled = false;
753 };
761 };
754 };
762 };
755
763
756
764
757 Notebook.prototype.cut_cell = function () {
765 Notebook.prototype.cut_cell = function () {
758 this.copy_cell();
766 this.copy_cell();
759 this.delete_cell();
767 this.delete_cell();
760 }
768 }
761
769
762 Notebook.prototype.copy_cell = function () {
770 Notebook.prototype.copy_cell = function () {
763 var cell = this.get_selected_cell();
771 var cell = this.get_selected_cell();
764 this.clipboard = cell.toJSON();
772 this.clipboard = cell.toJSON();
765 this.enable_paste();
773 this.enable_paste();
766 };
774 };
767
775
768
776
769 Notebook.prototype.paste_cell = function () {
777 Notebook.prototype.paste_cell = function () {
770 if (this.clipboard !== null && this.paste_enabled) {
778 if (this.clipboard !== null && this.paste_enabled) {
771 var cell_data = this.clipboard;
779 var cell_data = this.clipboard;
772 var new_cell = this.insert_cell_above(cell_data.cell_type);
780 var new_cell = this.insert_cell_above(cell_data.cell_type);
773 new_cell.fromJSON(cell_data);
781 new_cell.fromJSON(cell_data);
774 old_cell = this.get_next_cell(new_cell);
782 old_cell = this.get_next_cell(new_cell);
775 this.delete_cell(this.find_cell_index(old_cell));
783 this.delete_cell(this.find_cell_index(old_cell));
776 this.select(this.find_cell_index(new_cell));
784 this.select(this.find_cell_index(new_cell));
777 };
785 };
778 };
786 };
779
787
780
788
781 Notebook.prototype.paste_cell_above = function () {
789 Notebook.prototype.paste_cell_above = function () {
782 if (this.clipboard !== null && this.paste_enabled) {
790 if (this.clipboard !== null && this.paste_enabled) {
783 var cell_data = this.clipboard;
791 var cell_data = this.clipboard;
784 var new_cell = this.insert_cell_above(cell_data.cell_type);
792 var new_cell = this.insert_cell_above(cell_data.cell_type);
785 new_cell.fromJSON(cell_data);
793 new_cell.fromJSON(cell_data);
786 };
794 };
787 };
795 };
788
796
789
797
790 Notebook.prototype.paste_cell_below = function () {
798 Notebook.prototype.paste_cell_below = function () {
791 if (this.clipboard !== null && this.paste_enabled) {
799 if (this.clipboard !== null && this.paste_enabled) {
792 var cell_data = this.clipboard;
800 var cell_data = this.clipboard;
793 var new_cell = this.insert_cell_below(cell_data.cell_type);
801 var new_cell = this.insert_cell_below(cell_data.cell_type);
794 new_cell.fromJSON(cell_data);
802 new_cell.fromJSON(cell_data);
795 };
803 };
796 };
804 };
797
805
798
806
799 // Split/merge
807 // Split/merge
800
808
801 Notebook.prototype.split_cell = function () {
809 Notebook.prototype.split_cell = function () {
802 // Todo: implement spliting for other cell types.
810 // Todo: implement spliting for other cell types.
803 var cell = this.get_selected_cell();
811 var cell = this.get_selected_cell();
804 if (cell.is_splittable()) {
812 if (cell.is_splittable()) {
805 texta = cell.get_pre_cursor();
813 texta = cell.get_pre_cursor();
806 textb = cell.get_post_cursor();
814 textb = cell.get_post_cursor();
807 if (cell instanceof IPython.CodeCell) {
815 if (cell instanceof IPython.CodeCell) {
808 cell.set_text(texta);
816 cell.set_text(texta);
809 var new_cell = this.insert_cell_below('code');
817 var new_cell = this.insert_cell_below('code');
810 new_cell.set_text(textb);
818 new_cell.set_text(textb);
811 } else if (cell instanceof IPython.MarkdownCell) {
819 } else if (cell instanceof IPython.MarkdownCell) {
812 cell.set_text(texta);
820 cell.set_text(texta);
813 cell.render();
821 cell.render();
814 var new_cell = this.insert_cell_below('markdown');
822 var new_cell = this.insert_cell_below('markdown');
815 new_cell.edit(); // editor must be visible to call set_text
823 new_cell.edit(); // editor must be visible to call set_text
816 new_cell.set_text(textb);
824 new_cell.set_text(textb);
817 new_cell.render();
825 new_cell.render();
818 } else if (cell instanceof IPython.HTMLCell) {
826 } else if (cell instanceof IPython.HTMLCell) {
819 cell.set_text(texta);
827 cell.set_text(texta);
820 cell.render();
828 cell.render();
821 var new_cell = this.insert_cell_below('html');
829 var new_cell = this.insert_cell_below('html');
822 new_cell.edit(); // editor must be visible to call set_text
830 new_cell.edit(); // editor must be visible to call set_text
823 new_cell.set_text(textb);
831 new_cell.set_text(textb);
824 new_cell.render();
832 new_cell.render();
825 };
833 };
826 };
834 };
827 };
835 };
828
836
829
837
830 Notebook.prototype.merge_cell_above = function () {
838 Notebook.prototype.merge_cell_above = function () {
831 var index = this.get_selected_index();
839 var index = this.get_selected_index();
832 var cell = this.get_cell(index);
840 var cell = this.get_cell(index);
833 if (index > 0) {
841 if (index > 0) {
834 upper_cell = this.get_cell(index-1);
842 upper_cell = this.get_cell(index-1);
835 upper_text = upper_cell.get_text();
843 upper_text = upper_cell.get_text();
836 text = cell.get_text();
844 text = cell.get_text();
837 if (cell instanceof IPython.CodeCell) {
845 if (cell instanceof IPython.CodeCell) {
838 cell.set_text(upper_text+'\n'+text);
846 cell.set_text(upper_text+'\n'+text);
839 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
847 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
840 cell.edit();
848 cell.edit();
841 cell.set_text(upper_text+'\n'+text);
849 cell.set_text(upper_text+'\n'+text);
842 cell.render();
850 cell.render();
843 };
851 };
844 this.delete_cell(index-1);
852 this.delete_cell(index-1);
845 this.select(this.find_cell_index(cell));
853 this.select(this.find_cell_index(cell));
846 };
854 };
847 };
855 };
848
856
849
857
850 Notebook.prototype.merge_cell_below = function () {
858 Notebook.prototype.merge_cell_below = function () {
851 var index = this.get_selected_index();
859 var index = this.get_selected_index();
852 var cell = this.get_cell(index);
860 var cell = this.get_cell(index);
853 if (index < this.ncells()-1) {
861 if (index < this.ncells()-1) {
854 lower_cell = this.get_cell(index+1);
862 lower_cell = this.get_cell(index+1);
855 lower_text = lower_cell.get_text();
863 lower_text = lower_cell.get_text();
856 text = cell.get_text();
864 text = cell.get_text();
857 if (cell instanceof IPython.CodeCell) {
865 if (cell instanceof IPython.CodeCell) {
858 cell.set_text(text+'\n'+lower_text);
866 cell.set_text(text+'\n'+lower_text);
859 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
867 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
860 cell.edit();
868 cell.edit();
861 cell.set_text(text+'\n'+lower_text);
869 cell.set_text(text+'\n'+lower_text);
862 cell.render();
870 cell.render();
863 };
871 };
864 this.delete_cell(index+1);
872 this.delete_cell(index+1);
865 this.select(this.find_cell_index(cell));
873 this.select(this.find_cell_index(cell));
866 };
874 };
867 };
875 };
868
876
869
877
870 // Cell collapsing and output clearing
878 // Cell collapsing and output clearing
871
879
872 Notebook.prototype.collapse = function (index) {
880 Notebook.prototype.collapse = function (index) {
873 var i = this.index_or_selected(index);
881 var i = this.index_or_selected(index);
874 this.get_cell(i).collapse();
882 this.get_cell(i).collapse();
875 this.dirty = true;
883 this.dirty = true;
876 };
884 };
877
885
878
886
879 Notebook.prototype.expand = function (index) {
887 Notebook.prototype.expand = function (index) {
880 var i = this.index_or_selected(index);
888 var i = this.index_or_selected(index);
881 this.get_cell(i).expand();
889 this.get_cell(i).expand();
882 this.dirty = true;
890 this.dirty = true;
883 };
891 };
884
892
885
893
886 Notebook.prototype.toggle_output = function (index) {
894 Notebook.prototype.toggle_output = function (index) {
887 var i = this.index_or_selected(index);
895 var i = this.index_or_selected(index);
888 this.get_cell(i).toggle_output();
896 this.get_cell(i).toggle_output();
889 this.dirty = true;
897 this.dirty = true;
890 };
898 };
891
899
892
900
893 Notebook.prototype.toggle_output_scroll = function (index) {
901 Notebook.prototype.toggle_output_scroll = function (index) {
894 var i = this.index_or_selected(index);
902 var i = this.index_or_selected(index);
895 this.get_cell(i).toggle_output_scroll();
903 this.get_cell(i).toggle_output_scroll();
896 };
904 };
897
905
898
906
899 Notebook.prototype.collapse_all_output = function () {
907 Notebook.prototype.collapse_all_output = function () {
900 var ncells = this.ncells();
908 var ncells = this.ncells();
901 var cells = this.get_cells();
909 var cells = this.get_cells();
902 for (var i=0; i<ncells; i++) {
910 for (var i=0; i<ncells; i++) {
903 if (cells[i] instanceof IPython.CodeCell) {
911 if (cells[i] instanceof IPython.CodeCell) {
904 cells[i].output_area.collapse();
912 cells[i].output_area.collapse();
905 }
913 }
906 };
914 };
907 // this should not be set if the `collapse` key is removed from nbformat
915 // this should not be set if the `collapse` key is removed from nbformat
908 this.dirty = true;
916 this.dirty = true;
909 };
917 };
910
918
911
919
912 Notebook.prototype.scroll_all_output = function () {
920 Notebook.prototype.scroll_all_output = function () {
913 var ncells = this.ncells();
921 var ncells = this.ncells();
914 var cells = this.get_cells();
922 var cells = this.get_cells();
915 for (var i=0; i<ncells; i++) {
923 for (var i=0; i<ncells; i++) {
916 if (cells[i] instanceof IPython.CodeCell) {
924 if (cells[i] instanceof IPython.CodeCell) {
917 cells[i].output_area.expand();
925 cells[i].output_area.expand();
918 cells[i].output_area.scroll_if_long(20);
926 cells[i].output_area.scroll_if_long(20);
919 }
927 }
920 };
928 };
921 // this should not be set if the `collapse` key is removed from nbformat
929 // this should not be set if the `collapse` key is removed from nbformat
922 this.dirty = true;
930 this.dirty = true;
923 };
931 };
924
932
925
933
926 Notebook.prototype.expand_all_output = function () {
934 Notebook.prototype.expand_all_output = function () {
927 var ncells = this.ncells();
935 var ncells = this.ncells();
928 var cells = this.get_cells();
936 var cells = this.get_cells();
929 for (var i=0; i<ncells; i++) {
937 for (var i=0; i<ncells; i++) {
930 if (cells[i] instanceof IPython.CodeCell) {
938 if (cells[i] instanceof IPython.CodeCell) {
931 cells[i].output_area.expand();
939 cells[i].output_area.expand();
932 cells[i].output_area.unscroll_area();
940 cells[i].output_area.unscroll_area();
933 }
941 }
934 };
942 };
935 // this should not be set if the `collapse` key is removed from nbformat
943 // this should not be set if the `collapse` key is removed from nbformat
936 this.dirty = true;
944 this.dirty = true;
937 };
945 };
938
946
939
947
940 Notebook.prototype.clear_all_output = function () {
948 Notebook.prototype.clear_all_output = function () {
941 var ncells = this.ncells();
949 var ncells = this.ncells();
942 var cells = this.get_cells();
950 var cells = this.get_cells();
943 for (var i=0; i<ncells; i++) {
951 for (var i=0; i<ncells; i++) {
944 if (cells[i] instanceof IPython.CodeCell) {
952 if (cells[i] instanceof IPython.CodeCell) {
945 cells[i].clear_output(true,true,true);
953 cells[i].clear_output(true,true,true);
946 // Make all In[] prompts blank, as well
954 // Make all In[] prompts blank, as well
947 // TODO: make this configurable (via checkbox?)
955 // TODO: make this configurable (via checkbox?)
948 cells[i].set_input_prompt();
956 cells[i].set_input_prompt();
949 }
957 }
950 };
958 };
951 this.dirty = true;
959 this.dirty = true;
952 };
960 };
953
961
954
962
955 // Other cell functions: line numbers, ...
963 // Other cell functions: line numbers, ...
956
964
957 Notebook.prototype.cell_toggle_line_numbers = function() {
965 Notebook.prototype.cell_toggle_line_numbers = function() {
958 this.get_selected_cell().toggle_line_numbers();
966 this.get_selected_cell().toggle_line_numbers();
959 };
967 };
960
968
961 // Kernel related things
969 // Kernel related things
962
970
963 Notebook.prototype.start_kernel = function () {
971 Notebook.prototype.start_kernel = function () {
964 var base_url = $('body').data('baseKernelUrl') + "kernels";
972 var base_url = $('body').data('baseKernelUrl') + "kernels";
965 this.kernel = new IPython.Kernel(base_url);
973 this.kernel = new IPython.Kernel(base_url);
966 this.kernel.start(this.notebook_id);
974 this.kernel.start(this.notebook_id);
967 // Now that the kernel has been created, tell the CodeCells about it.
975 // Now that the kernel has been created, tell the CodeCells about it.
968 var ncells = this.ncells();
976 var ncells = this.ncells();
969 for (var i=0; i<ncells; i++) {
977 for (var i=0; i<ncells; i++) {
970 var cell = this.get_cell(i);
978 var cell = this.get_cell(i);
971 if (cell instanceof IPython.CodeCell) {
979 if (cell instanceof IPython.CodeCell) {
972 cell.set_kernel(this.kernel)
980 cell.set_kernel(this.kernel)
973 };
981 };
974 };
982 };
975 };
983 };
976
984
977
985
978 Notebook.prototype.restart_kernel = function () {
986 Notebook.prototype.restart_kernel = function () {
979 var that = this;
987 var that = this;
980 var dialog = $('<div/>');
988 var dialog = $('<div/>');
981 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
989 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
982 $(document).append(dialog);
990 $(document).append(dialog);
983 dialog.dialog({
991 dialog.dialog({
984 resizable: false,
992 resizable: false,
985 modal: true,
993 modal: true,
986 title: "Restart kernel or continue running?",
994 title: "Restart kernel or continue running?",
987 closeText: '',
995 closeText: '',
988 buttons : {
996 buttons : {
989 "Restart": function () {
997 "Restart": function () {
990 that.kernel.restart();
998 that.kernel.restart();
991 $(this).dialog('close');
999 $(this).dialog('close');
992 },
1000 },
993 "Continue running": function () {
1001 "Continue running": function () {
994 $(this).dialog('close');
1002 $(this).dialog('close');
995 }
1003 }
996 }
1004 }
997 });
1005 });
998 };
1006 };
999
1007
1000
1008
1001 Notebook.prototype.execute_selected_cell = function (options) {
1009 Notebook.prototype.execute_selected_cell = function (options) {
1002 // add_new: should a new cell be added if we are at the end of the nb
1010 // add_new: should a new cell be added if we are at the end of the nb
1003 // terminal: execute in terminal mode, which stays in the current cell
1011 // terminal: execute in terminal mode, which stays in the current cell
1004 default_options = {terminal: false, add_new: true};
1012 default_options = {terminal: false, add_new: true};
1005 $.extend(default_options, options);
1013 $.extend(default_options, options);
1006 var that = this;
1014 var that = this;
1007 var cell = that.get_selected_cell();
1015 var cell = that.get_selected_cell();
1008 var cell_index = that.find_cell_index(cell);
1016 var cell_index = that.find_cell_index(cell);
1009 if (cell instanceof IPython.CodeCell) {
1017 if (cell instanceof IPython.CodeCell) {
1010 cell.execute();
1018 cell.execute();
1011 } else if (cell instanceof IPython.HTMLCell) {
1019 } else if (cell instanceof IPython.HTMLCell) {
1012 cell.render();
1020 cell.render();
1013 }
1021 }
1014 if (default_options.terminal) {
1022 if (default_options.terminal) {
1015 cell.select_all();
1023 cell.select_all();
1016 } else {
1024 } else {
1017 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1025 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1018 that.insert_cell_below('code');
1026 that.insert_cell_below('code');
1019 // If we are adding a new cell at the end, scroll down to show it.
1027 // If we are adding a new cell at the end, scroll down to show it.
1020 that.scroll_to_bottom();
1028 that.scroll_to_bottom();
1021 } else {
1029 } else {
1022 that.select(cell_index+1);
1030 that.select(cell_index+1);
1023 };
1031 };
1024 };
1032 };
1025 this.dirty = true;
1033 this.dirty = true;
1026 };
1034 };
1027
1035
1028
1036
1029 Notebook.prototype.execute_all_cells = function () {
1037 Notebook.prototype.execute_all_cells = function () {
1030 var ncells = this.ncells();
1038 var ncells = this.ncells();
1031 for (var i=0; i<ncells; i++) {
1039 for (var i=0; i<ncells; i++) {
1032 this.select(i);
1040 this.select(i);
1033 this.execute_selected_cell({add_new:false});
1041 this.execute_selected_cell({add_new:false});
1034 };
1042 };
1035 this.scroll_to_bottom();
1043 this.scroll_to_bottom();
1036 };
1044 };
1037
1045
1038 // Persistance and loading
1046 // Persistance and loading
1039
1047
1040 Notebook.prototype.get_notebook_id = function () {
1048 Notebook.prototype.get_notebook_id = function () {
1041 return this.notebook_id;
1049 return this.notebook_id;
1042 };
1050 };
1043
1051
1044
1052
1045 Notebook.prototype.get_notebook_name = function () {
1053 Notebook.prototype.get_notebook_name = function () {
1046 return this.notebook_name;
1054 return this.notebook_name;
1047 };
1055 };
1048
1056
1049
1057
1050 Notebook.prototype.set_notebook_name = function (name) {
1058 Notebook.prototype.set_notebook_name = function (name) {
1051 this.notebook_name = name;
1059 this.notebook_name = name;
1052 };
1060 };
1053
1061
1054
1062
1055 Notebook.prototype.test_notebook_name = function (nbname) {
1063 Notebook.prototype.test_notebook_name = function (nbname) {
1056 nbname = nbname || '';
1064 nbname = nbname || '';
1057 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1065 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1058 return true;
1066 return true;
1059 } else {
1067 } else {
1060 return false;
1068 return false;
1061 };
1069 };
1062 };
1070 };
1063
1071
1064
1072
1065 Notebook.prototype.fromJSON = function (data) {
1073 Notebook.prototype.fromJSON = function (data) {
1066 var ncells = this.ncells();
1074 var ncells = this.ncells();
1067 var i;
1075 var i;
1068 for (i=0; i<ncells; i++) {
1076 for (i=0; i<ncells; i++) {
1069 // Always delete cell 0 as they get renumbered as they are deleted.
1077 // Always delete cell 0 as they get renumbered as they are deleted.
1070 this.delete_cell(0);
1078 this.delete_cell(0);
1071 };
1079 };
1072 // Save the metadata and name.
1080 // Save the metadata and name.
1073 this.metadata = data.metadata;
1081 this.metadata = data.metadata;
1074 this.notebook_name = data.metadata.name;
1082 this.notebook_name = data.metadata.name;
1075 // Only handle 1 worksheet for now.
1083 // Only handle 1 worksheet for now.
1076 var worksheet = data.worksheets[0];
1084 var worksheet = data.worksheets[0];
1077 if (worksheet !== undefined) {
1085 if (worksheet !== undefined) {
1078 if (worksheet.metadata) {
1086 if (worksheet.metadata) {
1079 this.worksheet_metadata = worksheet.metadata;
1087 this.worksheet_metadata = worksheet.metadata;
1080 }
1088 }
1081 var new_cells = worksheet.cells;
1089 var new_cells = worksheet.cells;
1082 ncells = new_cells.length;
1090 ncells = new_cells.length;
1083 var cell_data = null;
1091 var cell_data = null;
1084 var new_cell = null;
1092 var new_cell = null;
1085 for (i=0; i<ncells; i++) {
1093 for (i=0; i<ncells; i++) {
1086 cell_data = new_cells[i];
1094 cell_data = new_cells[i];
1087 // VERSIONHACK: plaintext -> raw
1095 // VERSIONHACK: plaintext -> raw
1088 // handle never-released plaintext name for raw cells
1096 // handle never-released plaintext name for raw cells
1089 if (cell_data.cell_type === 'plaintext'){
1097 if (cell_data.cell_type === 'plaintext'){
1090 cell_data.cell_type = 'raw';
1098 cell_data.cell_type = 'raw';
1091 }
1099 }
1092
1100
1093 new_cell = this.insert_cell_below(cell_data.cell_type);
1101 new_cell = this.insert_cell_below(cell_data.cell_type);
1094 new_cell.fromJSON(cell_data);
1102 new_cell.fromJSON(cell_data);
1095 };
1103 };
1096 };
1104 };
1097 if (data.worksheets.length > 1) {
1105 if (data.worksheets.length > 1) {
1098 var dialog = $('<div/>');
1106 var dialog = $('<div/>');
1099 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1107 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1100 "but this version of IPython can only handle the first. " +
1108 "but this version of IPython can only handle the first. " +
1101 "If you save this notebook, worksheets after the first will be lost."
1109 "If you save this notebook, worksheets after the first will be lost."
1102 );
1110 );
1103 this.element.append(dialog);
1111 this.element.append(dialog);
1104 dialog.dialog({
1112 dialog.dialog({
1105 resizable: false,
1113 resizable: false,
1106 modal: true,
1114 modal: true,
1107 title: "Multiple worksheets",
1115 title: "Multiple worksheets",
1108 closeText: "",
1116 closeText: "",
1109 close: function(event, ui) {$(this).dialog('destroy').remove();},
1117 close: function(event, ui) {$(this).dialog('destroy').remove();},
1110 buttons : {
1118 buttons : {
1111 "OK": function () {
1119 "OK": function () {
1112 $(this).dialog('close');
1120 $(this).dialog('close');
1113 }
1121 }
1114 },
1122 },
1115 width: 400
1123 width: 400
1116 });
1124 });
1117 }
1125 }
1118 };
1126 };
1119
1127
1120
1128
1121 Notebook.prototype.toJSON = function () {
1129 Notebook.prototype.toJSON = function () {
1122 var cells = this.get_cells();
1130 var cells = this.get_cells();
1123 var ncells = cells.length;
1131 var ncells = cells.length;
1124 var cell_array = new Array(ncells);
1132 var cell_array = new Array(ncells);
1125 for (var i=0; i<ncells; i++) {
1133 for (var i=0; i<ncells; i++) {
1126 cell_array[i] = cells[i].toJSON();
1134 cell_array[i] = cells[i].toJSON();
1127 };
1135 };
1128 var data = {
1136 var data = {
1129 // Only handle 1 worksheet for now.
1137 // Only handle 1 worksheet for now.
1130 worksheets : [{
1138 worksheets : [{
1131 cells: cell_array,
1139 cells: cell_array,
1132 metadata: this.worksheet_metadata
1140 metadata: this.worksheet_metadata
1133 }],
1141 }],
1134 metadata : this.metadata
1142 metadata : this.metadata
1135 };
1143 };
1136 return data;
1144 return data;
1137 };
1145 };
1138
1146
1139 Notebook.prototype.save_notebook = function () {
1147 Notebook.prototype.save_notebook = function () {
1140 // We may want to move the name/id/nbformat logic inside toJSON?
1148 // We may want to move the name/id/nbformat logic inside toJSON?
1141 var data = this.toJSON();
1149 var data = this.toJSON();
1142 data.metadata.name = this.notebook_name;
1150 data.metadata.name = this.notebook_name;
1143 data.nbformat = this.nbformat;
1151 data.nbformat = this.nbformat;
1144 data.nbformat_minor = this.nbformat_minor;
1152 data.nbformat_minor = this.nbformat_minor;
1145 // We do the call with settings so we can set cache to false.
1153 // We do the call with settings so we can set cache to false.
1146 var settings = {
1154 var settings = {
1147 processData : false,
1155 processData : false,
1148 cache : false,
1156 cache : false,
1149 type : "PUT",
1157 type : "PUT",
1150 data : JSON.stringify(data),
1158 data : JSON.stringify(data),
1151 headers : {'Content-Type': 'application/json'},
1159 headers : {'Content-Type': 'application/json'},
1152 success : $.proxy(this.save_notebook_success,this),
1160 success : $.proxy(this.save_notebook_success,this),
1153 error : $.proxy(this.save_notebook_error,this)
1161 error : $.proxy(this.save_notebook_error,this)
1154 };
1162 };
1155 $([IPython.events]).trigger('notebook_saving.Notebook');
1163 $([IPython.events]).trigger('notebook_saving.Notebook');
1156 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1164 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1157 $.ajax(url, settings);
1165 $.ajax(url, settings);
1158 };
1166 };
1159
1167
1160
1168
1161 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1169 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1162 this.dirty = false;
1170 this.dirty = false;
1163 $([IPython.events]).trigger('notebook_saved.Notebook');
1171 $([IPython.events]).trigger('notebook_saved.Notebook');
1164 };
1172 };
1165
1173
1166
1174
1167 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1175 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1168 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1176 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1169 };
1177 };
1170
1178
1171
1179
1172 Notebook.prototype.load_notebook = function (notebook_id) {
1180 Notebook.prototype.load_notebook = function (notebook_id) {
1173 var that = this;
1181 var that = this;
1174 this.notebook_id = notebook_id;
1182 this.notebook_id = notebook_id;
1175 // We do the call with settings so we can set cache to false.
1183 // We do the call with settings so we can set cache to false.
1176 var settings = {
1184 var settings = {
1177 processData : false,
1185 processData : false,
1178 cache : false,
1186 cache : false,
1179 type : "GET",
1187 type : "GET",
1180 dataType : "json",
1188 dataType : "json",
1181 success : $.proxy(this.load_notebook_success,this),
1189 success : $.proxy(this.load_notebook_success,this),
1182 error : $.proxy(this.load_notebook_error,this),
1190 error : $.proxy(this.load_notebook_error,this),
1183 };
1191 };
1184 $([IPython.events]).trigger('notebook_loading.Notebook');
1192 $([IPython.events]).trigger('notebook_loading.Notebook');
1185 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1193 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1186 $.ajax(url, settings);
1194 $.ajax(url, settings);
1187 };
1195 };
1188
1196
1189
1197
1190 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1198 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1191 this.fromJSON(data);
1199 this.fromJSON(data);
1192 if (this.ncells() === 0) {
1200 if (this.ncells() === 0) {
1193 this.insert_cell_below('code');
1201 this.insert_cell_below('code');
1194 };
1202 };
1195 this.dirty = false;
1203 this.dirty = false;
1196 this.select(0);
1204 this.select(0);
1197 this.scroll_to_top();
1205 this.scroll_to_top();
1198 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1206 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1199 msg = "This notebook has been converted from an older " +
1207 msg = "This notebook has been converted from an older " +
1200 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1208 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1201 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1209 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1202 "newer notebook format will be used and older verions of IPython " +
1210 "newer notebook format will be used and older verions of IPython " +
1203 "may not be able to read it. To keep the older version, close the " +
1211 "may not be able to read it. To keep the older version, close the " +
1204 "notebook without saving it.";
1212 "notebook without saving it.";
1205 var dialog = $('<div/>');
1213 var dialog = $('<div/>');
1206 dialog.html(msg);
1214 dialog.html(msg);
1207 this.element.append(dialog);
1215 this.element.append(dialog);
1208 dialog.dialog({
1216 dialog.dialog({
1209 resizable: false,
1217 resizable: false,
1210 modal: true,
1218 modal: true,
1211 title: "Notebook converted",
1219 title: "Notebook converted",
1212 closeText: "",
1220 closeText: "",
1213 close: function(event, ui) {$(this).dialog('destroy').remove();},
1221 close: function(event, ui) {$(this).dialog('destroy').remove();},
1214 buttons : {
1222 buttons : {
1215 "OK": function () {
1223 "OK": function () {
1216 $(this).dialog('close');
1224 $(this).dialog('close');
1217 }
1225 }
1218 },
1226 },
1219 width: 400
1227 width: 400
1220 });
1228 });
1221 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1229 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1222 var that = this;
1230 var that = this;
1223 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1231 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1224 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1232 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1225 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1233 msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1226 this_vs + ". You can still work with this notebook, but some features " +
1234 this_vs + ". You can still work with this notebook, but some features " +
1227 "introduced in later notebook versions may not be available."
1235 "introduced in later notebook versions may not be available."
1228
1236
1229 var dialog = $('<div/>');
1237 var dialog = $('<div/>');
1230 dialog.html(msg);
1238 dialog.html(msg);
1231 this.element.append(dialog);
1239 this.element.append(dialog);
1232 dialog.dialog({
1240 dialog.dialog({
1233 resizable: false,
1241 resizable: false,
1234 modal: true,
1242 modal: true,
1235 title: "Newer Notebook",
1243 title: "Newer Notebook",
1236 closeText: "",
1244 closeText: "",
1237 close: function(event, ui) {$(this).dialog('destroy').remove();},
1245 close: function(event, ui) {$(this).dialog('destroy').remove();},
1238 buttons : {
1246 buttons : {
1239 "OK": function () {
1247 "OK": function () {
1240 $(this).dialog('close');
1248 $(this).dialog('close');
1241 }
1249 }
1242 },
1250 },
1243 width: 400
1251 width: 400
1244 });
1252 });
1245
1253
1246 }
1254 }
1247 // Create the kernel after the notebook is completely loaded to prevent
1255 // Create the kernel after the notebook is completely loaded to prevent
1248 // code execution upon loading, which is a security risk.
1256 // code execution upon loading, which is a security risk.
1249 if (! this.read_only) {
1257 if (! this.read_only) {
1250 this.start_kernel();
1258 this.start_kernel();
1251 }
1259 }
1252 $([IPython.events]).trigger('notebook_loaded.Notebook');
1260 $([IPython.events]).trigger('notebook_loaded.Notebook');
1253 };
1261 };
1254
1262
1255
1263
1256 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1264 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1257 if (xhr.status === 500) {
1265 if (xhr.status === 500) {
1258 msg = "An error occurred while loading this notebook. Most likely " +
1266 msg = "An error occurred while loading this notebook. Most likely " +
1259 "this notebook is in a newer format than is supported by this " +
1267 "this notebook is in a newer format than is supported by this " +
1260 "version of IPython. This version can load notebook formats " +
1268 "version of IPython. This version can load notebook formats " +
1261 "v"+this.nbformat+" or earlier.";
1269 "v"+this.nbformat+" or earlier.";
1262 var dialog = $('<div/>');
1270 var dialog = $('<div/>');
1263 dialog.html(msg);
1271 dialog.html(msg);
1264 this.element.append(dialog);
1272 this.element.append(dialog);
1265 dialog.dialog({
1273 dialog.dialog({
1266 resizable: false,
1274 resizable: false,
1267 modal: true,
1275 modal: true,
1268 title: "Error loading notebook",
1276 title: "Error loading notebook",
1269 closeText: "",
1277 closeText: "",
1270 close: function(event, ui) {$(this).dialog('destroy').remove();},
1278 close: function(event, ui) {$(this).dialog('destroy').remove();},
1271 buttons : {
1279 buttons : {
1272 "OK": function () {
1280 "OK": function () {
1273 $(this).dialog('close');
1281 $(this).dialog('close');
1274 }
1282 }
1275 },
1283 },
1276 width: 400
1284 width: 400
1277 });
1285 });
1278 }
1286 }
1279 }
1287 }
1280
1288
1281 IPython.Notebook = Notebook;
1289 IPython.Notebook = Notebook;
1282
1290
1283
1291
1284 return IPython;
1292 return IPython;
1285
1293
1286 }(IPython));
1294 }(IPython));
1287
1295
@@ -1,71 +1,72 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // QuickHelp button
9 // QuickHelp button
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var QuickHelp = function (selector) {
14 var QuickHelp = function (selector) {
15 };
15 };
16
16
17 QuickHelp.prototype.show_keyboard_shortcuts = function () {
17 QuickHelp.prototype.show_keyboard_shortcuts = function () {
18 // toggles display of keyboard shortcut dialog
18 // toggles display of keyboard shortcut dialog
19 var that = this;
19 var that = this;
20 if ( this.shortcut_dialog ){
20 if ( this.shortcut_dialog ){
21 // if dialog is already shown, close it
21 // if dialog is already shown, close it
22 this.shortcut_dialog.dialog("close");
22 this.shortcut_dialog.dialog("close");
23 this.shortcut_dialog = null;
23 this.shortcut_dialog = null;
24 return;
24 return;
25 }
25 }
26 var dialog = $('<div/>');
26 var dialog = $('<div/>');
27 this.shortcut_dialog = dialog;
27 this.shortcut_dialog = dialog;
28 var shortcuts = [
28 var shortcuts = [
29 {key: 'Shift-Enter', help: 'run cell'},
29 {key: 'Shift-Enter', help: 'run cell'},
30 {key: 'Ctrl-Enter', help: 'run cell in-place'},
30 {key: 'Ctrl-Enter', help: 'run cell in-place'},
31 {key: 'Alt-Enter', help: 'run cell, insert below'},
31 {key: 'Ctrl-m x', help: 'cut cell'},
32 {key: 'Ctrl-m x', help: 'cut cell'},
32 {key: 'Ctrl-m c', help: 'copy cell'},
33 {key: 'Ctrl-m c', help: 'copy cell'},
33 {key: 'Ctrl-m v', help: 'paste cell'},
34 {key: 'Ctrl-m v', help: 'paste cell'},
34 {key: 'Ctrl-m d', help: 'delete cell'},
35 {key: 'Ctrl-m d', help: 'delete cell'},
35 {key: 'Ctrl-m a', help: 'insert cell above'},
36 {key: 'Ctrl-m a', help: 'insert cell above'},
36 {key: 'Ctrl-m b', help: 'insert cell below'},
37 {key: 'Ctrl-m b', help: 'insert cell below'},
37 {key: 'Ctrl-m o', help: 'toggle output'},
38 {key: 'Ctrl-m o', help: 'toggle output'},
38 {key: 'Ctrl-m O', help: 'toggle output scroll'},
39 {key: 'Ctrl-m O', help: 'toggle output scroll'},
39 {key: 'Ctrl-m l', help: 'toggle line numbers'},
40 {key: 'Ctrl-m l', help: 'toggle line numbers'},
40 {key: 'Ctrl-m s', help: 'save notebook'},
41 {key: 'Ctrl-m s', help: 'save notebook'},
41 {key: 'Ctrl-m j', help: 'move cell down'},
42 {key: 'Ctrl-m j', help: 'move cell down'},
42 {key: 'Ctrl-m k', help: 'move cell up'},
43 {key: 'Ctrl-m k', help: 'move cell up'},
43 {key: 'Ctrl-m y', help: 'code cell'},
44 {key: 'Ctrl-m y', help: 'code cell'},
44 {key: 'Ctrl-m m', help: 'markdown cell'},
45 {key: 'Ctrl-m m', help: 'markdown cell'},
45 {key: 'Ctrl-m t', help: 'raw cell'},
46 {key: 'Ctrl-m t', help: 'raw cell'},
46 {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
47 {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
47 {key: 'Ctrl-m p', help: 'select previous'},
48 {key: 'Ctrl-m p', help: 'select previous'},
48 {key: 'Ctrl-m n', help: 'select next'},
49 {key: 'Ctrl-m n', help: 'select next'},
49 {key: 'Ctrl-m i', help: 'interrupt kernel'},
50 {key: 'Ctrl-m i', help: 'interrupt kernel'},
50 {key: 'Ctrl-m .', help: 'restart kernel'},
51 {key: 'Ctrl-m .', help: 'restart kernel'},
51 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
52 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
52 ];
53 ];
53 for (var i=0; i<shortcuts.length; i++) {
54 for (var i=0; i<shortcuts.length; i++) {
54 dialog.append($('<div>').
55 dialog.append($('<div>').
55 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
56 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
56 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
57 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
57 );
58 );
58 };
59 };
59 dialog.bind('dialogclose', function(event) {
60 dialog.bind('dialogclose', function(event) {
60 // dialog has been closed, allow it to be drawn again.
61 // dialog has been closed, allow it to be drawn again.
61 that.shortcut_dialog = null;
62 that.shortcut_dialog = null;
62 });
63 });
63 dialog.dialog({title: 'Keyboard shortcuts', closeText: ''});
64 dialog.dialog({title: 'Keyboard shortcuts', closeText: ''});
64 };
65 };
65
66
66 // Set module variables
67 // Set module variables
67 IPython.QuickHelp = QuickHelp;
68 IPython.QuickHelp = QuickHelp;
68
69
69 return IPython;
70 return IPython;
70
71
71 }(IPython));
72 }(IPython));
@@ -1,432 +1,439 b''
1 .. _htmlnotebook:
1 .. _htmlnotebook:
2
2
3 =========================
3 =========================
4 An HTML Notebook IPython
4 An HTML Notebook IPython
5 =========================
5 =========================
6
6
7 .. seealso::
7 .. seealso::
8
8
9 :ref:`Installation requirements <installnotebook>` for the Notebook.
9 :ref:`Installation requirements <installnotebook>` for the Notebook.
10
10
11 The IPython Notebook consists of two related components:
11 The IPython Notebook consists of two related components:
12
12
13 * An JSON based Notebook document format for recording and distributing
13 * An JSON based Notebook document format for recording and distributing
14 Python code and rich text.
14 Python code and rich text.
15 * A web-based user interface for authoring and running notebook documents.
15 * A web-based user interface for authoring and running notebook documents.
16
16
17 The Notebook can be used by starting the Notebook server with the
17 The Notebook can be used by starting the Notebook server with the
18 command::
18 command::
19
19
20 $ ipython notebook
20 $ ipython notebook
21
21
22 Note that by default, the notebook doesn't load pylab, it's just a normal
22 Note that by default, the notebook doesn't load pylab, it's just a normal
23 IPython session like any other. If you want pylab support, you must use::
23 IPython session like any other. If you want pylab support, you must use::
24
24
25 $ ipython notebook --pylab
25 $ ipython notebook --pylab
26
26
27 which will behave similar to the terminal and Qt console versions, using your
27 which will behave similar to the terminal and Qt console versions, using your
28 default matplotlib backend and providing floating interactive plot windows. If
28 default matplotlib backend and providing floating interactive plot windows. If
29 you want inline figures, you must manually select the ``inline`` backend::
29 you want inline figures, you must manually select the ``inline`` backend::
30
30
31 $ ipython notebook --pylab inline
31 $ ipython notebook --pylab inline
32
32
33 This server uses the same ZeroMQ-based two process kernel architecture as
33 This server uses the same ZeroMQ-based two process kernel architecture as
34 the QT Console as well Tornado for serving HTTP/S requests. Some of the main
34 the QT Console as well Tornado for serving HTTP/S requests. Some of the main
35 features of the Notebook include:
35 features of the Notebook include:
36
36
37 * Display rich data (png/html/latex/svg) in the browser as a result of
37 * Display rich data (png/html/latex/svg) in the browser as a result of
38 computations.
38 computations.
39 * Compose text cells using HTML and Markdown.
39 * Compose text cells using HTML and Markdown.
40 * Import and export notebook documents in range of formats (.ipynb, .py).
40 * Import and export notebook documents in range of formats (.ipynb, .py).
41 * In browser syntax highlighting, tab completion and autoindentation.
41 * In browser syntax highlighting, tab completion and autoindentation.
42 * Inline matplotlib plots that can be stored in Notebook documents and opened
42 * Inline matplotlib plots that can be stored in Notebook documents and opened
43 later.
43 later.
44
44
45 See :ref:`our installation documentation <install_index>` for directions on
45 See :ref:`our installation documentation <install_index>` for directions on
46 how to install the notebook and its dependencies.
46 how to install the notebook and its dependencies.
47
47
48 .. note::
48 .. note::
49
49
50 You can start more than one notebook server at the same time, if you want to
50 You can start more than one notebook server at the same time, if you want to
51 work on notebooks in different directories. By default the first notebook
51 work on notebooks in different directories. By default the first notebook
52 server starts in port 8888, later notebooks search for random ports near
52 server starts in port 8888, later notebooks search for random ports near
53 that one. You can also manually specify the port with the ``--port``
53 that one. You can also manually specify the port with the ``--port``
54 option.
54 option.
55
55
56
56
57 Basic Usage
57 Basic Usage
58 ===========
58 ===========
59
59
60 The landing page of the notebook server application, which we call the IPython
60 The landing page of the notebook server application, which we call the IPython
61 Notebook *dashboard*, shows the notebooks currently available in the directory
61 Notebook *dashboard*, shows the notebooks currently available in the directory
62 in which the application was started, and allows you to create new notebooks.
62 in which the application was started, and allows you to create new notebooks.
63
63
64 A notebook is a combination of two things:
64 A notebook is a combination of two things:
65
65
66 1. An interactive session connected to an IPython kernel, controlled by a web
66 1. An interactive session connected to an IPython kernel, controlled by a web
67 application that can send input to the console and display many types of
67 application that can send input to the console and display many types of
68 output (text, graphics, mathematics and more). This is the same kernel used
68 output (text, graphics, mathematics and more). This is the same kernel used
69 by the :ref:`Qt console <qtconsole>`, but in this case the web console sends
69 by the :ref:`Qt console <qtconsole>`, but in this case the web console sends
70 input in persistent cells that you can edit in-place instead of the
70 input in persistent cells that you can edit in-place instead of the
71 vertically scrolling terminal style used by the Qt console.
71 vertically scrolling terminal style used by the Qt console.
72
72
73 2. A document that can save the inputs and outputs of the session as well as
73 2. A document that can save the inputs and outputs of the session as well as
74 additional text that accompanies the code but is not meant for execution.
74 additional text that accompanies the code but is not meant for execution.
75 In this way, notebook files serve as a complete computational record of a
75 In this way, notebook files serve as a complete computational record of a
76 session including explanatory text and mathematics, code and resulting
76 session including explanatory text and mathematics, code and resulting
77 figures. These documents are internally JSON files and are saved with the
77 figures. These documents are internally JSON files and are saved with the
78 ``.ipynb`` extension.
78 ``.ipynb`` extension.
79
79
80 If you have ever used the Mathematica or Sage notebooks (the latter is also
80 If you have ever used the Mathematica or Sage notebooks (the latter is also
81 web-based__) you should feel right at home. If you have not, you should be
81 web-based__) you should feel right at home. If you have not, you should be
82 able to learn how to use it in just a few minutes.
82 able to learn how to use it in just a few minutes.
83
83
84 .. __: http://sagenb.org
84 .. __: http://sagenb.org
85
85
86
86
87 Creating and editing notebooks
87 Creating and editing notebooks
88 ------------------------------
88 ------------------------------
89
89
90 You can create new notebooks from the dashboard with the ``New Notebook``
90 You can create new notebooks from the dashboard with the ``New Notebook``
91 button or open existing ones by clicking on their name. Once in a notebook,
91 button or open existing ones by clicking on their name. Once in a notebook,
92 your browser tab will reflect the name of that notebook (prefixed with "IPy:").
92 your browser tab will reflect the name of that notebook (prefixed with "IPy:").
93 The URL for that notebook is not meant to be human-readable and is *not*
93 The URL for that notebook is not meant to be human-readable and is *not*
94 persistent across invocations of the notebook server.
94 persistent across invocations of the notebook server.
95
95
96 You can also drag and drop into the area listing files any python file: it
96 You can also drag and drop into the area listing files any python file: it
97 will be imported into a notebook with the same name (but ``.ipynb`` extension)
97 will be imported into a notebook with the same name (but ``.ipynb`` extension)
98 located in the directory where the notebook server was started. This notebook
98 located in the directory where the notebook server was started. This notebook
99 will consist of a single cell with all the code in the file, which you can
99 will consist of a single cell with all the code in the file, which you can
100 later manually partition into individual cells for gradual execution, add text
100 later manually partition into individual cells for gradual execution, add text
101 and graphics, etc.
101 and graphics, etc.
102
102
103
103
104 Workflow and limitations
104 Workflow and limitations
105 ------------------------
105 ------------------------
106
106
107 The normal workflow in a notebook is quite similar to a normal IPython session,
107 The normal workflow in a notebook is quite similar to a normal IPython session,
108 with the difference that you can edit a cell in-place multiple times until you
108 with the difference that you can edit a cell in-place multiple times until you
109 obtain the desired results rather than having to rerun separate scripts with
109 obtain the desired results rather than having to rerun separate scripts with
110 the ``%run`` magic (though magics also work in the notebook). Typically
110 the ``%run`` magic (though magics also work in the notebook). Typically
111 you'll work on a problem in pieces, organizing related pieces into cells and
111 you'll work on a problem in pieces, organizing related pieces into cells and
112 moving forward as previous parts work correctly. This is much more convenient
112 moving forward as previous parts work correctly. This is much more convenient
113 for interactive exploration than breaking up a computation into scripts that
113 for interactive exploration than breaking up a computation into scripts that
114 must be executed together, especially if parts of them take a long time to run
114 must be executed together, especially if parts of them take a long time to run
115 (In the traditional terminal-based IPython, you can use tricks with namespaces
115 (In the traditional terminal-based IPython, you can use tricks with namespaces
116 and ``%run -i`` to achieve this capability, but we think the notebook is a more
116 and ``%run -i`` to achieve this capability, but we think the notebook is a more
117 natural solution for that kind of problem).
117 natural solution for that kind of problem).
118
118
119 The only significant limitation the notebook currently has, compared to the qt
119 The only significant limitation the notebook currently has, compared to the qt
120 console, is that it can not run any code that expects input from the kernel
120 console, is that it can not run any code that expects input from the kernel
121 (such as scripts that call :func:`raw_input`). Very importantly, this means
121 (such as scripts that call :func:`raw_input`). Very importantly, this means
122 that the ``%debug`` magic does *not* work in the notebook! We intend to
122 that the ``%debug`` magic does *not* work in the notebook! We intend to
123 correct this limitation, but in the meantime, there is a way to debug problems
123 correct this limitation, but in the meantime, there is a way to debug problems
124 in the notebook: you can attach a Qt console to your existing notebook kernel,
124 in the notebook: you can attach a Qt console to your existing notebook kernel,
125 and run ``%debug`` from the Qt console. If your notebook is running on a local
125 and run ``%debug`` from the Qt console. If your notebook is running on a local
126 computer (i.e. if you are accessing it via your localhost address at
126 computer (i.e. if you are accessing it via your localhost address at
127 127.0.0.1), you can just type ``%qtconsole`` in the notebook and a Qt console
127 127.0.0.1), you can just type ``%qtconsole`` in the notebook and a Qt console
128 will open up connected to that same kernel.
128 will open up connected to that same kernel.
129
129
130 In general, the notebook server prints the full details of how to connect to
130 In general, the notebook server prints the full details of how to connect to
131 each kernel at the terminal, with lines like::
131 each kernel at the terminal, with lines like::
132
132
133 [IPKernelApp] To connect another client to this kernel, use:
133 [IPKernelApp] To connect another client to this kernel, use:
134 [IPKernelApp] --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
134 [IPKernelApp] --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
135
135
136 This is the name of a JSON file that contains all the port and validation
136 This is the name of a JSON file that contains all the port and validation
137 information necessary to connect to the kernel. You can manually start a
137 information necessary to connect to the kernel. You can manually start a
138 qt console with::
138 qt console with::
139
139
140 ipython qtconsole --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
140 ipython qtconsole --existing kernel-3bb93edd-6b5a-455c-99c8-3b658f45dde5.json
141
141
142 and if you only have a single kernel running, simply typing::
142 and if you only have a single kernel running, simply typing::
143
143
144 ipython qtconsole --existing
144 ipython qtconsole --existing
145
145
146 will automatically find it (it will always find the most recently started
146 will automatically find it (it will always find the most recently started
147 kernel if there is more than one). You can also request this connection data
147 kernel if there is more than one). You can also request this connection data
148 by typing ``%connect_info``; this will print the same file information as well
148 by typing ``%connect_info``; this will print the same file information as well
149 as the content of the JSON data structure it contains.
149 as the content of the JSON data structure it contains.
150
150
151
151
152 Text input
152 Text input
153 ----------
153 ----------
154
154
155 In addition to code cells and the output they produce (such as figures), you
155 In addition to code cells and the output they produce (such as figures), you
156 can also type text not meant for execution. To type text, change the type of a
156 can also type text not meant for execution. To type text, change the type of a
157 cell from ``Code`` to ``Markdown`` by using the button or the :kbd:`Ctrl-m m`
157 cell from ``Code`` to ``Markdown`` by using the button or the :kbd:`Ctrl-m m`
158 keybinding (see below). You can then type any text in Markdown_ syntax, as
158 keybinding (see below). You can then type any text in Markdown_ syntax, as
159 well as mathematical expressions if you use ``$...$`` for inline math or
159 well as mathematical expressions if you use ``$...$`` for inline math or
160 ``$$...$$`` for displayed math.
160 ``$$...$$`` for displayed math.
161
161
162
162
163 Exporting a notebook and importing existing scripts
163 Exporting a notebook and importing existing scripts
164 ---------------------------------------------------
164 ---------------------------------------------------
165
165
166 If you want to provide others with a static HTML or PDF view of your notebook,
166 If you want to provide others with a static HTML or PDF view of your notebook,
167 use the ``Print`` button. This opens a static view of the document, which you
167 use the ``Print`` button. This opens a static view of the document, which you
168 can print to PDF using your operating system's facilities, or save to a file
168 can print to PDF using your operating system's facilities, or save to a file
169 with your web browser's 'Save' option (note that typically, this will create
169 with your web browser's 'Save' option (note that typically, this will create
170 both an html file *and* a directory called `notebook_name_files` next to it
170 both an html file *and* a directory called `notebook_name_files` next to it
171 that contains all the necessary style information, so if you intend to share
171 that contains all the necessary style information, so if you intend to share
172 this, you must send the directory along with the main html file).
172 this, you must send the directory along with the main html file).
173
173
174 The `Download` button lets you save a notebook file to the Download area
174 The `Download` button lets you save a notebook file to the Download area
175 configured by your web browser (particularly useful if you are running the
175 configured by your web browser (particularly useful if you are running the
176 notebook server on a remote host and need a file locally). The notebook is
176 notebook server on a remote host and need a file locally). The notebook is
177 saved by default with the ``.ipynb`` extension and the files contain JSON data
177 saved by default with the ``.ipynb`` extension and the files contain JSON data
178 that is not meant for human editing or consumption. But you can always export
178 that is not meant for human editing or consumption. But you can always export
179 the input part of a notebook to a plain python script by choosing Python format
179 the input part of a notebook to a plain python script by choosing Python format
180 in the `Download` drop list. This removes all output and saves the text cells
180 in the `Download` drop list. This removes all output and saves the text cells
181 in comment areas. See ref:`below <notebook_format>` for more details on the
181 in comment areas. See ref:`below <notebook_format>` for more details on the
182 notebook format.
182 notebook format.
183
183
184 The notebook can also *import* ``.py`` files as notebooks, by dragging and
184 The notebook can also *import* ``.py`` files as notebooks, by dragging and
185 dropping the file into the notebook dashboard file list area. By default, the
185 dropping the file into the notebook dashboard file list area. By default, the
186 entire contents of the file will be loaded into a single code cell. But if
186 entire contents of the file will be loaded into a single code cell. But if
187 prior to import, you manually add the ``# <nbformat>2</nbformat>`` marker at
187 prior to import, you manually add the ``# <nbformat>2</nbformat>`` marker at
188 the start and then add separators for text/code cells, you can get a cleaner
188 the start and then add separators for text/code cells, you can get a cleaner
189 import with the file broken into individual cells.
189 import with the file broken into individual cells.
190
190
191 .. warning::
191 .. warning::
192
192
193 While in simple cases you can roundtrip a notebook to Python, edit the
193 While in simple cases you can roundtrip a notebook to Python, edit the
194 python file and import it back without loss of main content, this is in
194 python file and import it back without loss of main content, this is in
195 general *not guaranteed to work at all*. First, there is extra metadata
195 general *not guaranteed to work at all*. First, there is extra metadata
196 saved in the notebook that may not be saved to the ``.py`` format. And as
196 saved in the notebook that may not be saved to the ``.py`` format. And as
197 the notebook format evolves in complexity, there will be attributes of the
197 the notebook format evolves in complexity, there will be attributes of the
198 notebook that will not survive a roundtrip through the Python form. You
198 notebook that will not survive a roundtrip through the Python form. You
199 should think of the Python format as a way to output a script version of a
199 should think of the Python format as a way to output a script version of a
200 notebook and the import capabilities as a way to load existing code to get a
200 notebook and the import capabilities as a way to load existing code to get a
201 notebook started. But the Python version is *not* an alternate notebook
201 notebook started. But the Python version is *not* an alternate notebook
202 format.
202 format.
203
203
204
204
205 Importing or executing a notebook as a normal Python file
205 Importing or executing a notebook as a normal Python file
206 ---------------------------------------------------------
206 ---------------------------------------------------------
207
207
208 The native format of the notebook, a file with a ``.ipynb`` extension, is a
208 The native format of the notebook, a file with a ``.ipynb`` extension, is a
209 JSON container of all the input and output of the notebook, and therefore not
209 JSON container of all the input and output of the notebook, and therefore not
210 valid Python by itself. This means that by default, you can not import a
210 valid Python by itself. This means that by default, you can not import a
211 notebook or execute it as a normal python script. But if you want use
211 notebook or execute it as a normal python script. But if you want use
212 notebooks as regular Python files, you can start the notebook server with::
212 notebooks as regular Python files, you can start the notebook server with::
213
213
214 ipython notebook --script
214 ipython notebook --script
215
215
216 or you can set this option permanently in your configuration file with::
216 or you can set this option permanently in your configuration file with::
217
217
218 c.NotebookManager.save_script=True
218 c.NotebookManager.save_script=True
219
219
220 This will instruct the notebook server to save the ``.py`` export of each
220 This will instruct the notebook server to save the ``.py`` export of each
221 notebook adjacent to the ``.ipynb`` at every save. These files can be
221 notebook adjacent to the ``.ipynb`` at every save. These files can be
222 ``%run``, imported from regular IPython sessions or other notebooks, or
222 ``%run``, imported from regular IPython sessions or other notebooks, or
223 executed at the command-line as normal Python files. Since we export the raw
223 executed at the command-line as normal Python files. Since we export the raw
224 code you have typed, for these files to be importable from other code you will
224 code you have typed, for these files to be importable from other code you will
225 have to avoid using syntax such as ``%magics`` and other IPython-specific
225 have to avoid using syntax such as ``%magics`` and other IPython-specific
226 extensions to the language.
226 extensions to the language.
227
227
228 In regular practice, the standard way to differentiate importable code from the
228 In regular practice, the standard way to differentiate importable code from the
229 'executable' part of a script is to put at the bottom::
229 'executable' part of a script is to put at the bottom::
230
230
231 if __name__ == '__main__':
231 if __name__ == '__main__':
232 # rest of the code...
232 # rest of the code...
233
233
234 Since all cells in the notebook are run as top-level code, you'll need to
234 Since all cells in the notebook are run as top-level code, you'll need to
235 similarly protect *all* cells that you do not want executed when other scripts
235 similarly protect *all* cells that you do not want executed when other scripts
236 try to import your notebook. A convenient shortand for this is to define early
236 try to import your notebook. A convenient shortand for this is to define early
237 on::
237 on::
238
238
239 script = __name__ == '__main__'
239 script = __name__ == '__main__'
240
240
241 and then on any cell that you need to protect, use::
241 and then on any cell that you need to protect, use::
242
242
243 if script:
243 if script:
244 # rest of the cell...
244 # rest of the cell...
245
245
246
246
247 Keyboard use
247 Keyboard use
248 ------------
248 ------------
249
249
250 All actions in the notebook can be achieved with the mouse, but we have also
250 All actions in the notebook can be achieved with the mouse, but we have also
251 added keyboard shortcuts for the most common ones, so that productive use of
251 added keyboard shortcuts for the most common ones, so that productive use of
252 the notebook can be achieved with minimal mouse intervention. The main
252 the notebook can be achieved with minimal mouse intervention. The main
253 key bindings you need to remember are:
253 key bindings you need to remember are:
254
254
255 * :kbd:`Shift-Enter`: execute the current cell (similar to the Qt console),
255 * :kbd:`Shift-Enter`: execute the current cell (similar to the Qt console),
256 show output (if any) and create a new cell below. Note that in the notebook,
256 show output (if any) and jump to the next cell below. If :kbd:`Shift-Enter`
257 simply using :kbd:`Enter` *never* forces execution, it simply inserts a new
257 was invoked on the last input line, a new code cell will also be created. Note
258 line in the current cell. Therefore, in the notebook you must always use
258 that in the notebook, simply using :kbd:`Enter` *never* forces execution,
259 :kbd:`Shift-Enter` to get execution (or use the mouse and click on the ``Run
259 it simply inserts a new line in the current cell. Therefore, in the notebook
260 Selected`` button).
260 you must always use :kbd:`Shift-Enter` to get execution (or use the mouse and
261
261 click on the ``Run Selected`` button).
262
263 * :kbd:`Alt-Enter`: this combination is similar to the previous one, with the
264 exception that, if the next cell below is not empty, a new code cell will be
265 added to the notebook, even if the cell execution happens not in the last cell.
266 In this regard, :kbd:`Alt-Enter`: is simply a shortcut for the :kbd:`Shift-Enter`,
267 :kbd:`Ctrl-m a` sequence.
268
262 * :kbd:`Ctrl-Enter`: execute the current cell in "terminal mode", where any
269 * :kbd:`Ctrl-Enter`: execute the current cell in "terminal mode", where any
263 output is shown but the cursor stays in the current cell, whose input
270 output is shown but the cursor stays in the current cell, whose input
264 area is flushed empty. This is convenient to do quick in-place experiments
271 area is flushed empty. This is convenient to do quick in-place experiments
265 or query things like filesystem content without creating additional cells you
272 or query things like filesystem content without creating additional cells you
266 may not want saved in your notebook.
273 may not want saved in your notebook.
267
274
268 * :kbd:`Ctrl-m`: this is the prefix for all other keybindings, which consist
275 * :kbd:`Ctrl-m`: this is the prefix for all other keybindings, which consist
269 of an additional single letter. Type :kbd:`Ctrl-m h` (that is, the sole
276 of an additional single letter. Type :kbd:`Ctrl-m h` (that is, the sole
270 letter :kbd:`h` after :kbd:`Ctrl-m`) and IPython will show you the remaining
277 letter :kbd:`h` after :kbd:`Ctrl-m`) and IPython will show you the remaining
271 available keybindings.
278 available keybindings.
272
279
273
280
274 .. _notebook_security:
281 .. _notebook_security:
275
282
276 Security
283 Security
277 ========
284 ========
278
285
279 You can protect your notebook server with a simple single-password by
286 You can protect your notebook server with a simple single-password by
280 setting the :attr:`NotebookApp.password` configurable. You can prepare a
287 setting the :attr:`NotebookApp.password` configurable. You can prepare a
281 hashed password using the function :func:`IPython.lib.security.passwd`:
288 hashed password using the function :func:`IPython.lib.security.passwd`:
282
289
283 .. sourcecode:: ipython
290 .. sourcecode:: ipython
284
291
285 In [1]: from IPython.lib import passwd
292 In [1]: from IPython.lib import passwd
286 In [2]: passwd()
293 In [2]: passwd()
287 Enter password:
294 Enter password:
288 Verify password:
295 Verify password:
289 Out[2]: 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
296 Out[2]: 'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
290
297
291 .. note::
298 .. note::
292
299
293 :func:`~IPython.lib.security.passwd` can also take the password as a string
300 :func:`~IPython.lib.security.passwd` can also take the password as a string
294 argument. **Do not** pass it as an argument inside an IPython session, as it
301 argument. **Do not** pass it as an argument inside an IPython session, as it
295 will be saved in your input history.
302 will be saved in your input history.
296
303
297 You can then add this to your :file:`ipython_notebook_config.py`, e.g.::
304 You can then add this to your :file:`ipython_notebook_config.py`, e.g.::
298
305
299 # Password to use for web authentication
306 # Password to use for web authentication
300 c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
307 c.NotebookApp.password = u'sha1:67c9e60bb8b6:9ffede0825894254b2e042ea597d771089e11aed'
301
308
302 When using a password, it is a good idea to also use SSL, so that your password
309 When using a password, it is a good idea to also use SSL, so that your password
303 is not sent unencrypted by your browser. You can start the notebook to
310 is not sent unencrypted by your browser. You can start the notebook to
304 communicate via a secure protocol mode using a self-signed certificate by
311 communicate via a secure protocol mode using a self-signed certificate by
305 typing::
312 typing::
306
313
307 $ ipython notebook --certfile=mycert.pem
314 $ ipython notebook --certfile=mycert.pem
308
315
309 .. note::
316 .. note::
310
317
311 A self-signed certificate can be generated with openssl. For example, the
318 A self-signed certificate can be generated with openssl. For example, the
312 following command will create a certificate valid for 365 days with both
319 following command will create a certificate valid for 365 days with both
313 the key and certificate data written to the same file::
320 the key and certificate data written to the same file::
314
321
315 $ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
322 $ openssl req -x509 -nodes -days 365 -newkey rsa:1024 -keyout mycert.pem -out mycert.pem
316
323
317 Your browser will warn you of a dangerous certificate because it is
324 Your browser will warn you of a dangerous certificate because it is
318 self-signed. If you want to have a fully compliant certificate that will not
325 self-signed. If you want to have a fully compliant certificate that will not
319 raise warnings, it is possible (but rather involved) to obtain one for free,
326 raise warnings, it is possible (but rather involved) to obtain one for free,
320 `as explained in detailed in this tutorial`__.
327 `as explained in detailed in this tutorial`__.
321
328
322 .. __: http://arstechnica.com/security/news/2009/12/how-to-get-set-with-a-secure-sertificate-for-free.ars
329 .. __: http://arstechnica.com/security/news/2009/12/how-to-get-set-with-a-secure-sertificate-for-free.ars
323
330
324 Keep in mind that when you enable SSL support, you'll need to access the
331 Keep in mind that when you enable SSL support, you'll need to access the
325 notebook server over ``https://``, not over plain ``http://``. The startup
332 notebook server over ``https://``, not over plain ``http://``. The startup
326 message from the server prints this, but it's easy to overlook and think the
333 message from the server prints this, but it's easy to overlook and think the
327 server is for some reason non-responsive.
334 server is for some reason non-responsive.
328
335
329
336
330 Quick Howto: running a public notebook server
337 Quick Howto: running a public notebook server
331 =============================================
338 =============================================
332
339
333 If you want to access your notebook server remotely with just a web browser,
340 If you want to access your notebook server remotely with just a web browser,
334 here is a quick set of instructions. Start by creating a certificate file and
341 here is a quick set of instructions. Start by creating a certificate file and
335 a hashed password as explained above. Then, create a custom profile for the
342 a hashed password as explained above. Then, create a custom profile for the
336 notebook. At the command line, type::
343 notebook. At the command line, type::
337
344
338 ipython profile create nbserver
345 ipython profile create nbserver
339
346
340 In the profile directory, edit the file ``ipython_notebook_config.py``. By
347 In the profile directory, edit the file ``ipython_notebook_config.py``. By
341 default the file has all fields commented, the minimum set you need to
348 default the file has all fields commented, the minimum set you need to
342 uncomment and edit is here::
349 uncomment and edit is here::
343
350
344 c = get_config()
351 c = get_config()
345
352
346 # Kernel config
353 # Kernel config
347 c.IPKernelApp.pylab = 'inline' # if you want plotting support always
354 c.IPKernelApp.pylab = 'inline' # if you want plotting support always
348
355
349 # Notebook config
356 # Notebook config
350 c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
357 c.NotebookApp.certfile = u'/absolute/path/to/your/certificate/mycert.pem'
351 c.NotebookApp.ip = '*'
358 c.NotebookApp.ip = '*'
352 c.NotebookApp.open_browser = False
359 c.NotebookApp.open_browser = False
353 c.NotebookApp.password = u'sha1:bcd259ccf...your hashed password here'
360 c.NotebookApp.password = u'sha1:bcd259ccf...your hashed password here'
354 # It's a good idea to put it on a known, fixed port
361 # It's a good idea to put it on a known, fixed port
355 c.NotebookApp.port = 9999
362 c.NotebookApp.port = 9999
356
363
357 You can then start the notebook and access it later by pointing your browser to
364 You can then start the notebook and access it later by pointing your browser to
358 ``https://your.host.com:9999`` with ``ipython notebook --profile=nbserver``.
365 ``https://your.host.com:9999`` with ``ipython notebook --profile=nbserver``.
359
366
360 Running with a different URL prefix
367 Running with a different URL prefix
361 ===================================
368 ===================================
362
369
363 The notebook dashboard (i.e. the default landing page with an overview
370 The notebook dashboard (i.e. the default landing page with an overview
364 of all your notebooks) typically lives at a URL path of
371 of all your notebooks) typically lives at a URL path of
365 "http://localhost:8888/". If you want to have it, and the rest of the
372 "http://localhost:8888/". If you want to have it, and the rest of the
366 notebook, live under a sub-directory,
373 notebook, live under a sub-directory,
367 e.g. "http://localhost:8888/ipython/", you can do so with
374 e.g. "http://localhost:8888/ipython/", you can do so with
368 configuration options like these (see above for instructions about
375 configuration options like these (see above for instructions about
369 modifying ``ipython_notebook_config.py``)::
376 modifying ``ipython_notebook_config.py``)::
370
377
371 c.NotebookApp.base_project_url = '/ipython/'
378 c.NotebookApp.base_project_url = '/ipython/'
372 c.NotebookApp.base_kernel_url = '/ipython/'
379 c.NotebookApp.base_kernel_url = '/ipython/'
373 c.NotebookApp.webapp_settings = {'static_url_prefix':'/ipython/static/'}
380 c.NotebookApp.webapp_settings = {'static_url_prefix':'/ipython/static/'}
374
381
375 .. _notebook_format:
382 .. _notebook_format:
376
383
377 The notebook format
384 The notebook format
378 ===================
385 ===================
379
386
380 The notebooks themselves are JSON files with an ``ipynb`` extension, formatted
387 The notebooks themselves are JSON files with an ``ipynb`` extension, formatted
381 as legibly as possible with minimal extra indentation and cell content broken
388 as legibly as possible with minimal extra indentation and cell content broken
382 across lines to make them reasonably friendly to use in version-control
389 across lines to make them reasonably friendly to use in version-control
383 workflows. You should be very careful if you ever edit manually this JSON
390 workflows. You should be very careful if you ever edit manually this JSON
384 data, as it is extremely easy to corrupt its internal structure and make the
391 data, as it is extremely easy to corrupt its internal structure and make the
385 file impossible to load. In general, you should consider the notebook as a
392 file impossible to load. In general, you should consider the notebook as a
386 file meant only to be edited by IPython itself, not for hand-editing.
393 file meant only to be edited by IPython itself, not for hand-editing.
387
394
388 .. note::
395 .. note::
389
396
390 Binary data such as figures are directly saved in the JSON file. This
397 Binary data such as figures are directly saved in the JSON file. This
391 provides convenient single-file portability but means the files can be
398 provides convenient single-file portability but means the files can be
392 large and diffs of binary data aren't very meaningful. Since the binary
399 large and diffs of binary data aren't very meaningful. Since the binary
393 blobs are encoded in a single line they only affect one line of the diff
400 blobs are encoded in a single line they only affect one line of the diff
394 output, but they are typically very long lines. You can use the
401 output, but they are typically very long lines. You can use the
395 'ClearAll' button to remove all output from a notebook prior to
402 'ClearAll' button to remove all output from a notebook prior to
396 committing it to version control, if this is a concern.
403 committing it to version control, if this is a concern.
397
404
398 The notebook server can also generate a pure-python version of your notebook,
405 The notebook server can also generate a pure-python version of your notebook,
399 by clicking on the 'Download' button and selecting ``py`` as the format. This
406 by clicking on the 'Download' button and selecting ``py`` as the format. This
400 file will contain all the code cells from your notebook verbatim, and all text
407 file will contain all the code cells from your notebook verbatim, and all text
401 cells prepended with a comment marker. The separation between code and text
408 cells prepended with a comment marker. The separation between code and text
402 cells is indicated with special comments and there is a header indicating the
409 cells is indicated with special comments and there is a header indicating the
403 format version. All output is stripped out when exporting to python.
410 format version. All output is stripped out when exporting to python.
404
411
405 Here is an example of a simple notebook with one text cell and one code input
412 Here is an example of a simple notebook with one text cell and one code input
406 cell, when exported to python format::
413 cell, when exported to python format::
407
414
408 # <nbformat>2</nbformat>
415 # <nbformat>2</nbformat>
409
416
410 # <markdowncell>
417 # <markdowncell>
411
418
412 # A text cell
419 # A text cell
413
420
414 # <codecell>
421 # <codecell>
415
422
416 print "hello IPython"
423 print "hello IPython"
417
424
418
425
419 Known Issues
426 Known Issues
420 ============
427 ============
421
428
422 When behind a proxy, especially if your system or browser is set to autodetect
429 When behind a proxy, especially if your system or browser is set to autodetect
423 the proxy, the html notebook might fail to connect to the server's websockets,
430 the proxy, the html notebook might fail to connect to the server's websockets,
424 and present you with a warning at startup. In this case, you need to configure
431 and present you with a warning at startup. In this case, you need to configure
425 your system not to use the proxy for the server's address.
432 your system not to use the proxy for the server's address.
426
433
427 In Firefox, for example, go to the Preferences panel, Advanced section,
434 In Firefox, for example, go to the Preferences panel, Advanced section,
428 Network tab, click 'Settings...', and add the address of the notebook server
435 Network tab, click 'Settings...', and add the address of the notebook server
429 to the 'No proxy for' field.
436 to the 'No proxy for' field.
430
437
431
438
432 .. _Markdown: http://daringfireball.net/projects/markdown/basics
439 .. _Markdown: http://daringfireball.net/projects/markdown/basics
General Comments 0
You need to be logged in to leave comments. Login now