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