##// END OF EJS Templates
Merge pull request #3076 from dwyde/improve-notebook-js-yuidoc...
Matthias Bussonnier -
r10042:699a45a9 merge
parent child Browse files
Show More
@@ -1,48 +1,48 b''
1 Documentation
1 Documentation
2 =============
2 =============
3
3
4 How to Build/ view the doc for javascript.
4 How to Build/ view the doc for javascript.
5
5
6 Javascript documentation should follow a style close to JSDoc one, so you
6 Javascript documentation should follow a style close to JSDoc one, so you
7 should be able to build them with your favorite documentation builder.
7 should be able to build them with your favorite documentation builder.
8
8
9 Still the documentation comment are mainly written to be read with YUI doc.
9 Still the documentation comment are mainly written to be read with YUI doc.
10
10
11 You can either build a static version, or start a YUIdoc server that will live
11 You can either build a static version, or start a YUIdoc server that will live
12 update the doc at every page request.
12 update the doc at every page request.
13
13
14 To do so, you will need to install YUIdoc.
14 To do so, you will need to install YUIdoc.
15
15
16 ## Install NodeJS
16 ## Install NodeJS
17
17
18 Node is a browser less javascript interpreter. To install it please refer to
18 Node is a browser less javascript interpreter. To install it please refer to
19 the documentation for your platform. Install also NPM (node package manager) if
19 the documentation for your platform. Install also NPM (node package manager) if
20 it does not come bundled with it.
20 it does not come bundled with it.
21
21
22 ## Get YUIdoc
22 ## Get YUIdoc
23
23
24 npm does by default install package in `./node_modules` instead of doing a
24 npm does by default install package in `./node_modules` instead of doing a
25 system wide install. I'll leave you to yuidoc docs if you want to make a system
25 system wide install. I'll leave you to yuidoc docs if you want to make a system
26 wide install.
26 wide install.
27
27
28 First, cd into js directory :
28 First, cd into js directory :
29 ```bash
29 ```bash
30 cd IPython/frontend/html/notebook/static/js/
30 cd IPython/frontend/html/notebook/static/js/
31 # install yuidoc
31 # install yuidoc
32 npm install yuidoc
32 npm install yuidocjs
33 ```
33 ```
34
34
35
35
36 ## Run YUIdoc server
36 ## Run YUIdoc server
37
37
38 From IPython/frontend/html/notebook/static/js/
38 From IPython/frontend/html/notebook/static/js/
39 ```bash
39 ```bash
40 # run yuidoc for install dir
40 # run yuidoc for install dir
41 ./node_modules/yuidocjs/lib/cli.js --server .
41 ./node_modules/yuidocjs/lib/cli.js --server .
42 ```
42 ```
43
43
44 Follow the instruction and the documentation should be available on localhost:3000
44 Follow the instruction and the documentation should be available on localhost:3000
45
45
46 Omitting `--server` will build a static version in the `out` folder by default.
46 Omitting `--server` will build a static version in the `out` folder by default.
47
47
48
48
@@ -1,1415 +1,1782 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 /**
18 * A notebook contains and manages cells.
19 *
20 * @class Notebook
21 * @constructor
22 * @param {String} selector A jQuery selector for the notebook's DOM element
23 * @param {Object} [options] A config object
24 */
17 var Notebook = function (selector, options) {
25 var Notebook = function (selector, options) {
18 var options = options || {};
26 var options = options || {};
19 this._baseProjectUrl = options.baseProjectUrl;
27 this._baseProjectUrl = options.baseProjectUrl;
20 this.read_only = options.read_only || IPython.read_only;
28 this.read_only = options.read_only || IPython.read_only;
21
29
22 this.element = $(selector);
30 this.element = $(selector);
23 this.element.scroll();
31 this.element.scroll();
24 this.element.data("notebook", this);
32 this.element.data("notebook", this);
25 this.next_prompt_number = 1;
33 this.next_prompt_number = 1;
26 this.kernel = null;
34 this.kernel = null;
27 this.clipboard = null;
35 this.clipboard = null;
28 this.undelete_backup = null;
36 this.undelete_backup = null;
29 this.undelete_index = null;
37 this.undelete_index = null;
30 this.undelete_below = false;
38 this.undelete_below = false;
31 this.paste_enabled = false;
39 this.paste_enabled = false;
32 this.dirty = false;
40 this.dirty = false;
33 this.metadata = {};
41 this.metadata = {};
34 // single worksheet for now
42 // single worksheet for now
35 this.worksheet_metadata = {};
43 this.worksheet_metadata = {};
36 this.control_key_active = false;
44 this.control_key_active = false;
37 this.notebook_id = null;
45 this.notebook_id = null;
38 this.notebook_name = null;
46 this.notebook_name = null;
39 this.notebook_name_blacklist_re = /[\/\\:]/;
47 this.notebook_name_blacklist_re = /[\/\\:]/;
40 this.nbformat = 3 // Increment this when changing the nbformat
48 this.nbformat = 3 // Increment this when changing the nbformat
41 this.nbformat_minor = 0 // Increment this when changing the nbformat
49 this.nbformat_minor = 0 // Increment this when changing the nbformat
42 this.style();
50 this.style();
43 this.create_elements();
51 this.create_elements();
44 this.bind_events();
52 this.bind_events();
45 };
53 };
46
54
47
55 /**
56 * Tweak the notebook's CSS style.
57 *
58 * @method style
59 */
48 Notebook.prototype.style = function () {
60 Notebook.prototype.style = function () {
49 $('div#notebook').addClass('border-box-sizing');
61 $('div#notebook').addClass('border-box-sizing');
50 };
62 };
51
63
64 /**
65 * Get the root URL of the notebook server.
66 *
67 * @method baseProjectUrl
68 * @return {String} The base project URL
69 */
52 Notebook.prototype.baseProjectUrl = function(){
70 Notebook.prototype.baseProjectUrl = function(){
53 return this._baseProjectUrl || $('body').data('baseProjectUrl');
71 return this._baseProjectUrl || $('body').data('baseProjectUrl');
54 };
72 };
55
73
56
74 /**
75 * Create an HTML and CSS representation of the notebook.
76 *
77 * @method create_elements
78 */
57 Notebook.prototype.create_elements = function () {
79 Notebook.prototype.create_elements = function () {
58 // We add this end_space div to the end of the notebook div to:
80 // We add this end_space div to the end of the notebook div to:
59 // i) provide a margin between the last cell and the end of the notebook
81 // i) provide a margin between the last cell and the end of the notebook
60 // ii) to prevent the div from scrolling up when the last cell is being
82 // ii) to prevent the div from scrolling up when the last cell is being
61 // edited, but is too low on the page, which browsers will do automatically.
83 // edited, but is too low on the page, which browsers will do automatically.
62 var that = this;
84 var that = this;
63 var end_space = $('<div/>').addClass('end_space').height("30%");
85 var end_space = $('<div/>').addClass('end_space').height("30%");
64 end_space.dblclick(function (e) {
86 end_space.dblclick(function (e) {
65 if (that.read_only) return;
87 if (that.read_only) return;
66 var ncells = that.ncells();
88 var ncells = that.ncells();
67 that.insert_cell_below('code',ncells-1);
89 that.insert_cell_below('code',ncells-1);
68 });
90 });
69 this.element.append(end_space);
91 this.element.append(end_space);
70 $('div#notebook').addClass('border-box-sizing');
92 $('div#notebook').addClass('border-box-sizing');
71 };
93 };
72
94
73
95 /**
96 * Bind JavaScript events: key presses and custom IPython events.
97 *
98 * @method bind_events
99 */
74 Notebook.prototype.bind_events = function () {
100 Notebook.prototype.bind_events = function () {
75 var that = this;
101 var that = this;
76
102
77 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
103 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
78 var index = that.find_cell_index(data.cell);
104 var index = that.find_cell_index(data.cell);
79 var new_cell = that.insert_cell_below('code',index);
105 var new_cell = that.insert_cell_below('code',index);
80 new_cell.set_text(data.text);
106 new_cell.set_text(data.text);
81 that.dirty = true;
107 that.dirty = true;
82 });
108 });
83
109
84 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
110 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
85 that.dirty = data.value;
111 that.dirty = data.value;
86 });
112 });
87
113
88 $([IPython.events]).on('select.Cell', function (event, data) {
114 $([IPython.events]).on('select.Cell', function (event, data) {
89 var index = that.find_cell_index(data.cell);
115 var index = that.find_cell_index(data.cell);
90 that.select(index);
116 that.select(index);
91 });
117 });
92
118
93
119
94 $(document).keydown(function (event) {
120 $(document).keydown(function (event) {
95 // console.log(event);
121 // console.log(event);
96 if (that.read_only) return true;
122 if (that.read_only) return true;
97
123
98 // Save (CTRL+S) or (AppleKey+S)
124 // Save (CTRL+S) or (AppleKey+S)
99 //metaKey = applekey on mac
125 //metaKey = applekey on mac
100 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
126 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
101 that.save_notebook();
127 that.save_notebook();
102 event.preventDefault();
128 event.preventDefault();
103 return false;
129 return false;
104 } else if (event.which === key.ESC) {
130 } else if (event.which === key.ESC) {
105 // Intercept escape at highest level to avoid closing
131 // Intercept escape at highest level to avoid closing
106 // websocket connection with firefox
132 // websocket connection with firefox
107 event.preventDefault();
133 event.preventDefault();
108 } else if (event.which === key.SHIFT) {
134 } else if (event.which === key.SHIFT) {
109 // ignore shift keydown
135 // ignore shift keydown
110 return true;
136 return true;
111 }
137 }
112 if (event.which === key.UPARROW && !event.shiftKey) {
138 if (event.which === key.UPARROW && !event.shiftKey) {
113 var cell = that.get_selected_cell();
139 var cell = that.get_selected_cell();
114 if (cell.at_top()) {
140 if (cell.at_top()) {
115 event.preventDefault();
141 event.preventDefault();
116 that.select_prev();
142 that.select_prev();
117 };
143 };
118 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
144 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
119 var cell = that.get_selected_cell();
145 var cell = that.get_selected_cell();
120 if (cell.at_bottom()) {
146 if (cell.at_bottom()) {
121 event.preventDefault();
147 event.preventDefault();
122 that.select_next();
148 that.select_next();
123 };
149 };
124 } else if (event.which === key.ENTER && event.shiftKey) {
150 } else if (event.which === key.ENTER && event.shiftKey) {
125 that.execute_selected_cell();
151 that.execute_selected_cell();
126 return false;
152 return false;
127 } else if (event.which === key.ENTER && event.altKey) {
153 } else if (event.which === key.ENTER && event.altKey) {
128 // Execute code cell, and insert new in place
154 // Execute code cell, and insert new in place
129 that.execute_selected_cell();
155 that.execute_selected_cell();
130 // Only insert a new cell, if we ended up in an already populated cell
156 // Only insert a new cell, if we ended up in an already populated cell
131 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
157 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
132 that.insert_cell_above('code');
158 that.insert_cell_above('code');
133 }
159 }
134 return false;
160 return false;
135 } else if (event.which === key.ENTER && event.ctrlKey) {
161 } else if (event.which === key.ENTER && event.ctrlKey) {
136 that.execute_selected_cell({terminal:true});
162 that.execute_selected_cell({terminal:true});
137 return false;
163 return false;
138 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
164 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
139 that.control_key_active = true;
165 that.control_key_active = true;
140 return false;
166 return false;
141 } else if (event.which === 88 && that.control_key_active) {
167 } else if (event.which === 88 && that.control_key_active) {
142 // Cut selected cell = x
168 // Cut selected cell = x
143 that.cut_cell();
169 that.cut_cell();
144 that.control_key_active = false;
170 that.control_key_active = false;
145 return false;
171 return false;
146 } else if (event.which === 67 && that.control_key_active) {
172 } else if (event.which === 67 && that.control_key_active) {
147 // Copy selected cell = c
173 // Copy selected cell = c
148 that.copy_cell();
174 that.copy_cell();
149 that.control_key_active = false;
175 that.control_key_active = false;
150 return false;
176 return false;
151 } else if (event.which === 86 && that.control_key_active) {
177 } else if (event.which === 86 && that.control_key_active) {
152 // Paste below selected cell = v
178 // Paste below selected cell = v
153 that.paste_cell_below();
179 that.paste_cell_below();
154 that.control_key_active = false;
180 that.control_key_active = false;
155 return false;
181 return false;
156 } else if (event.which === 68 && that.control_key_active) {
182 } else if (event.which === 68 && that.control_key_active) {
157 // Delete selected cell = d
183 // Delete selected cell = d
158 that.delete_cell();
184 that.delete_cell();
159 that.control_key_active = false;
185 that.control_key_active = false;
160 return false;
186 return false;
161 } else if (event.which === 65 && that.control_key_active) {
187 } else if (event.which === 65 && that.control_key_active) {
162 // Insert code cell above selected = a
188 // Insert code cell above selected = a
163 that.insert_cell_above('code');
189 that.insert_cell_above('code');
164 that.control_key_active = false;
190 that.control_key_active = false;
165 return false;
191 return false;
166 } else if (event.which === 66 && that.control_key_active) {
192 } else if (event.which === 66 && that.control_key_active) {
167 // Insert code cell below selected = b
193 // Insert code cell below selected = b
168 that.insert_cell_below('code');
194 that.insert_cell_below('code');
169 that.control_key_active = false;
195 that.control_key_active = false;
170 return false;
196 return false;
171 } else if (event.which === 89 && that.control_key_active) {
197 } else if (event.which === 89 && that.control_key_active) {
172 // To code = y
198 // To code = y
173 that.to_code();
199 that.to_code();
174 that.control_key_active = false;
200 that.control_key_active = false;
175 return false;
201 return false;
176 } else if (event.which === 77 && that.control_key_active) {
202 } else if (event.which === 77 && that.control_key_active) {
177 // To markdown = m
203 // To markdown = m
178 that.to_markdown();
204 that.to_markdown();
179 that.control_key_active = false;
205 that.control_key_active = false;
180 return false;
206 return false;
181 } else if (event.which === 84 && that.control_key_active) {
207 } else if (event.which === 84 && that.control_key_active) {
182 // To Raw = t
208 // To Raw = t
183 that.to_raw();
209 that.to_raw();
184 that.control_key_active = false;
210 that.control_key_active = false;
185 return false;
211 return false;
186 } else if (event.which === 49 && that.control_key_active) {
212 } else if (event.which === 49 && that.control_key_active) {
187 // To Heading 1 = 1
213 // To Heading 1 = 1
188 that.to_heading(undefined, 1);
214 that.to_heading(undefined, 1);
189 that.control_key_active = false;
215 that.control_key_active = false;
190 return false;
216 return false;
191 } else if (event.which === 50 && that.control_key_active) {
217 } else if (event.which === 50 && that.control_key_active) {
192 // To Heading 2 = 2
218 // To Heading 2 = 2
193 that.to_heading(undefined, 2);
219 that.to_heading(undefined, 2);
194 that.control_key_active = false;
220 that.control_key_active = false;
195 return false;
221 return false;
196 } else if (event.which === 51 && that.control_key_active) {
222 } else if (event.which === 51 && that.control_key_active) {
197 // To Heading 3 = 3
223 // To Heading 3 = 3
198 that.to_heading(undefined, 3);
224 that.to_heading(undefined, 3);
199 that.control_key_active = false;
225 that.control_key_active = false;
200 return false;
226 return false;
201 } else if (event.which === 52 && that.control_key_active) {
227 } else if (event.which === 52 && that.control_key_active) {
202 // To Heading 4 = 4
228 // To Heading 4 = 4
203 that.to_heading(undefined, 4);
229 that.to_heading(undefined, 4);
204 that.control_key_active = false;
230 that.control_key_active = false;
205 return false;
231 return false;
206 } else if (event.which === 53 && that.control_key_active) {
232 } else if (event.which === 53 && that.control_key_active) {
207 // To Heading 5 = 5
233 // To Heading 5 = 5
208 that.to_heading(undefined, 5);
234 that.to_heading(undefined, 5);
209 that.control_key_active = false;
235 that.control_key_active = false;
210 return false;
236 return false;
211 } else if (event.which === 54 && that.control_key_active) {
237 } else if (event.which === 54 && that.control_key_active) {
212 // To Heading 6 = 6
238 // To Heading 6 = 6
213 that.to_heading(undefined, 6);
239 that.to_heading(undefined, 6);
214 that.control_key_active = false;
240 that.control_key_active = false;
215 return false;
241 return false;
216 } else if (event.which === 79 && that.control_key_active) {
242 } else if (event.which === 79 && that.control_key_active) {
217 // Toggle output = o
243 // Toggle output = o
218 if (event.shiftKey){
244 if (event.shiftKey){
219 that.toggle_output_scroll();
245 that.toggle_output_scroll();
220 } else {
246 } else {
221 that.toggle_output();
247 that.toggle_output();
222 }
248 }
223 that.control_key_active = false;
249 that.control_key_active = false;
224 return false;
250 return false;
225 } else if (event.which === 83 && that.control_key_active) {
251 } else if (event.which === 83 && that.control_key_active) {
226 // Save notebook = s
252 // Save notebook = s
227 that.save_notebook();
253 that.save_notebook();
228 that.control_key_active = false;
254 that.control_key_active = false;
229 return false;
255 return false;
230 } else if (event.which === 74 && that.control_key_active) {
256 } else if (event.which === 74 && that.control_key_active) {
231 // Move cell down = j
257 // Move cell down = j
232 that.move_cell_down();
258 that.move_cell_down();
233 that.control_key_active = false;
259 that.control_key_active = false;
234 return false;
260 return false;
235 } else if (event.which === 75 && that.control_key_active) {
261 } else if (event.which === 75 && that.control_key_active) {
236 // Move cell up = k
262 // Move cell up = k
237 that.move_cell_up();
263 that.move_cell_up();
238 that.control_key_active = false;
264 that.control_key_active = false;
239 return false;
265 return false;
240 } else if (event.which === 80 && that.control_key_active) {
266 } else if (event.which === 80 && that.control_key_active) {
241 // Select previous = p
267 // Select previous = p
242 that.select_prev();
268 that.select_prev();
243 that.control_key_active = false;
269 that.control_key_active = false;
244 return false;
270 return false;
245 } else if (event.which === 78 && that.control_key_active) {
271 } else if (event.which === 78 && that.control_key_active) {
246 // Select next = n
272 // Select next = n
247 that.select_next();
273 that.select_next();
248 that.control_key_active = false;
274 that.control_key_active = false;
249 return false;
275 return false;
250 } else if (event.which === 76 && that.control_key_active) {
276 } else if (event.which === 76 && that.control_key_active) {
251 // Toggle line numbers = l
277 // Toggle line numbers = l
252 that.cell_toggle_line_numbers();
278 that.cell_toggle_line_numbers();
253 that.control_key_active = false;
279 that.control_key_active = false;
254 return false;
280 return false;
255 } else if (event.which === 73 && that.control_key_active) {
281 } else if (event.which === 73 && that.control_key_active) {
256 // Interrupt kernel = i
282 // Interrupt kernel = i
257 that.kernel.interrupt();
283 that.kernel.interrupt();
258 that.control_key_active = false;
284 that.control_key_active = false;
259 return false;
285 return false;
260 } else if (event.which === 190 && that.control_key_active) {
286 } else if (event.which === 190 && that.control_key_active) {
261 // Restart kernel = . # matches qt console
287 // Restart kernel = . # matches qt console
262 that.restart_kernel();
288 that.restart_kernel();
263 that.control_key_active = false;
289 that.control_key_active = false;
264 return false;
290 return false;
265 } else if (event.which === 72 && that.control_key_active) {
291 } else if (event.which === 72 && that.control_key_active) {
266 // Show keyboard shortcuts = h
292 // Show keyboard shortcuts = h
267 IPython.quick_help.show_keyboard_shortcuts();
293 IPython.quick_help.show_keyboard_shortcuts();
268 that.control_key_active = false;
294 that.control_key_active = false;
269 return false;
295 return false;
270 } else if (event.which === 90 && that.control_key_active) {
296 } else if (event.which === 90 && that.control_key_active) {
271 // Undo last cell delete = z
297 // Undo last cell delete = z
272 that.undelete();
298 that.undelete();
273 that.control_key_active = false;
299 that.control_key_active = false;
274 return false;
300 return false;
275 } else if (that.control_key_active) {
301 } else if (that.control_key_active) {
276 that.control_key_active = false;
302 that.control_key_active = false;
277 return true;
303 return true;
278 };
304 };
279 return true;
305 return true;
280 });
306 });
281
307
282 var collapse_time = function(time){
308 var collapse_time = function(time){
283 var app_height = $('#ipython-main-app').height(); // content height
309 var app_height = $('#ipython-main-app').height(); // content height
284 var splitter_height = $('div#pager_splitter').outerHeight(true);
310 var splitter_height = $('div#pager_splitter').outerHeight(true);
285 var new_height = app_height - splitter_height;
311 var new_height = app_height - splitter_height;
286 that.element.animate({height : new_height + 'px'}, time);
312 that.element.animate({height : new_height + 'px'}, time);
287 }
313 }
288
314
289 this.element.bind('collapse_pager', function (event,extrap) {
315 this.element.bind('collapse_pager', function (event,extrap) {
290 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
316 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
291 collapse_time(time);
317 collapse_time(time);
292 });
318 });
293
319
294 var expand_time = function(time) {
320 var expand_time = function(time) {
295 var app_height = $('#ipython-main-app').height(); // content height
321 var app_height = $('#ipython-main-app').height(); // content height
296 var splitter_height = $('div#pager_splitter').outerHeight(true);
322 var splitter_height = $('div#pager_splitter').outerHeight(true);
297 var pager_height = $('div#pager').outerHeight(true);
323 var pager_height = $('div#pager').outerHeight(true);
298 var new_height = app_height - pager_height - splitter_height;
324 var new_height = app_height - pager_height - splitter_height;
299 that.element.animate({height : new_height + 'px'}, time);
325 that.element.animate({height : new_height + 'px'}, time);
300 }
326 }
301
327
302 this.element.bind('expand_pager', function (event, extrap) {
328 this.element.bind('expand_pager', function (event, extrap) {
303 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
329 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
304 expand_time(time);
330 expand_time(time);
305 });
331 });
306
332
307 $(window).bind('beforeunload', function () {
333 $(window).bind('beforeunload', function () {
308 // TODO: Make killing the kernel configurable.
334 // TODO: Make killing the kernel configurable.
309 var kill_kernel = false;
335 var kill_kernel = false;
310 if (kill_kernel) {
336 if (kill_kernel) {
311 that.kernel.kill();
337 that.kernel.kill();
312 }
338 }
313 if (that.dirty && ! that.read_only) {
339 if (that.dirty && ! that.read_only) {
314 return "You have unsaved changes that will be lost if you leave this page.";
340 return "You have unsaved changes that will be lost if you leave this page.";
315 };
341 };
316 // Null is the *only* return value that will make the browser not
342 // Null is the *only* return value that will make the browser not
317 // pop up the "don't leave" dialog.
343 // pop up the "don't leave" dialog.
318 return null;
344 return null;
319 });
345 });
320 };
346 };
321
347
348 /**
349 * Scroll the top of the page to a given cell.
350 *
351 * @method scroll_to_cell
352 * @param {Number} cell_number An index of the cell to view
353 * @param {Number} time Animation time in milliseconds
354 * @return {Number} Pixel offset from the top of the container
355 */
322 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
356 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
323 var cells = this.get_cells();
357 var cells = this.get_cells();
324 var time = time || 0;
358 var time = time || 0;
325 cell_number = Math.min(cells.length-1,cell_number);
359 cell_number = Math.min(cells.length-1,cell_number);
326 cell_number = Math.max(0 ,cell_number);
360 cell_number = Math.max(0 ,cell_number);
327 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
361 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
328 this.element.animate({scrollTop:scroll_value}, time);
362 this.element.animate({scrollTop:scroll_value}, time);
329 return scroll_value;
363 return scroll_value;
330 };
364 };
331
365
332
366 /**
367 * Scroll to the bottom of the page.
368 *
369 * @method scroll_to_bottom
370 */
333 Notebook.prototype.scroll_to_bottom = function () {
371 Notebook.prototype.scroll_to_bottom = function () {
334 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
372 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
335 };
373 };
336
374
337
375 /**
376 * Scroll to the top of the page.
377 *
378 * @method scroll_to_top
379 */
338 Notebook.prototype.scroll_to_top = function () {
380 Notebook.prototype.scroll_to_top = function () {
339 this.element.animate({scrollTop:0}, 0);
381 this.element.animate({scrollTop:0}, 0);
340 };
382 };
341
383
342
384
343 // Cell indexing, retrieval, etc.
385 // Cell indexing, retrieval, etc.
344
386
387 /**
388 * Get all cell elements in the notebook.
389 *
390 * @method get_cell_elements
391 * @return {jQuery} A selector of all cell elements
392 */
345 Notebook.prototype.get_cell_elements = function () {
393 Notebook.prototype.get_cell_elements = function () {
346 return this.element.children("div.cell");
394 return this.element.children("div.cell");
347 };
395 };
348
396
349
397 /**
398 * Get a particular cell element.
399 *
400 * @method get_cell_element
401 * @param {Number} index An index of a cell to select
402 * @return {jQuery} A selector of the given cell.
403 */
350 Notebook.prototype.get_cell_element = function (index) {
404 Notebook.prototype.get_cell_element = function (index) {
351 var result = null;
405 var result = null;
352 var e = this.get_cell_elements().eq(index);
406 var e = this.get_cell_elements().eq(index);
353 if (e.length !== 0) {
407 if (e.length !== 0) {
354 result = e;
408 result = e;
355 }
409 }
356 return result;
410 return result;
357 };
411 };
358
412
359
413 /**
360 Notebook.prototype.ncells = function (cell) {
414 * Count the cells in this notebook.
415 *
416 * @method ncells
417 * @return {Number} The number of cells in this notebook
418 */
419 Notebook.prototype.ncells = function () {
361 return this.get_cell_elements().length;
420 return this.get_cell_elements().length;
362 };
421 };
363
422
364
423 /**
424 * Get all Cell objects in this notebook.
425 *
426 * @method get_cells
427 * @return {Array} This notebook's Cell objects
428 */
365 // TODO: we are often calling cells as cells()[i], which we should optimize
429 // TODO: we are often calling cells as cells()[i], which we should optimize
366 // to cells(i) or a new method.
430 // to cells(i) or a new method.
367 Notebook.prototype.get_cells = function () {
431 Notebook.prototype.get_cells = function () {
368 return this.get_cell_elements().toArray().map(function (e) {
432 return this.get_cell_elements().toArray().map(function (e) {
369 return $(e).data("cell");
433 return $(e).data("cell");
370 });
434 });
371 };
435 };
372
436
373
437 /**
438 * Get a Cell object from this notebook.
439 *
440 * @method get_cell
441 * @param {Number} index An index of a cell to retrieve
442 * @return {Cell} A particular cell
443 */
374 Notebook.prototype.get_cell = function (index) {
444 Notebook.prototype.get_cell = function (index) {
375 var result = null;
445 var result = null;
376 var ce = this.get_cell_element(index);
446 var ce = this.get_cell_element(index);
377 if (ce !== null) {
447 if (ce !== null) {
378 result = ce.data('cell');
448 result = ce.data('cell');
379 }
449 }
380 return result;
450 return result;
381 }
451 }
382
452
383
453 /**
454 * Get the cell below a given cell.
455 *
456 * @method get_next_cell
457 * @param {Cell} cell The provided cell
458 * @return {Cell} The next cell
459 */
384 Notebook.prototype.get_next_cell = function (cell) {
460 Notebook.prototype.get_next_cell = function (cell) {
385 var result = null;
461 var result = null;
386 var index = this.find_cell_index(cell);
462 var index = this.find_cell_index(cell);
387 if (this.is_valid_cell_index(index+1)) {
463 if (this.is_valid_cell_index(index+1)) {
388 result = this.get_cell(index+1);
464 result = this.get_cell(index+1);
389 }
465 }
390 return result;
466 return result;
391 }
467 }
392
468
393
469 /**
470 * Get the cell above a given cell.
471 *
472 * @method get_prev_cell
473 * @param {Cell} cell The provided cell
474 * @return {Cell} The previous cell
475 */
394 Notebook.prototype.get_prev_cell = function (cell) {
476 Notebook.prototype.get_prev_cell = function (cell) {
477 // TODO: off-by-one
478 // nb.get_prev_cell(nb.get_cell(1)) is null
395 var result = null;
479 var result = null;
396 var index = this.find_cell_index(cell);
480 var index = this.find_cell_index(cell);
397 if (index !== null && index > 1) {
481 if (index !== null && index > 1) {
398 result = this.get_cell(index-1);
482 result = this.get_cell(index-1);
399 }
483 }
400 return result;
484 return result;
401 }
485 }
402
486
487 /**
488 * Get the numeric index of a given cell.
489 *
490 * @method find_cell_index
491 * @param {Cell} cell The provided cell
492 * @return {Number} The cell's numeric index
493 */
403 Notebook.prototype.find_cell_index = function (cell) {
494 Notebook.prototype.find_cell_index = function (cell) {
404 var result = null;
495 var result = null;
405 this.get_cell_elements().filter(function (index) {
496 this.get_cell_elements().filter(function (index) {
406 if ($(this).data("cell") === cell) {
497 if ($(this).data("cell") === cell) {
407 result = index;
498 result = index;
408 };
499 };
409 });
500 });
410 return result;
501 return result;
411 };
502 };
412
503
413
504 /**
505 * Get a given index , or the selected index if none is provided.
506 *
507 * @method index_or_selected
508 * @param {Number} index A cell's index
509 * @return {Number} The given index, or selected index if none is provided.
510 */
414 Notebook.prototype.index_or_selected = function (index) {
511 Notebook.prototype.index_or_selected = function (index) {
415 var i;
512 var i;
416 if (index === undefined || index === null) {
513 if (index === undefined || index === null) {
417 i = this.get_selected_index();
514 i = this.get_selected_index();
418 if (i === null) {
515 if (i === null) {
419 i = 0;
516 i = 0;
420 }
517 }
421 } else {
518 } else {
422 i = index;
519 i = index;
423 }
520 }
424 return i;
521 return i;
425 };
522 };
426
523
427
524 /**
525 * Get the currently selected cell.
526 * @method get_selected_cell
527 * @return {Cell} The selected cell
528 */
428 Notebook.prototype.get_selected_cell = function () {
529 Notebook.prototype.get_selected_cell = function () {
429 var index = this.get_selected_index();
530 var index = this.get_selected_index();
430 return this.get_cell(index);
531 return this.get_cell(index);
431 };
532 };
432
533
433
534 /**
535 * Check whether a cell index is valid.
536 *
537 * @method is_valid_cell_index
538 * @param {Number} index A cell index
539 * @return True if the index is valid, false otherwise
540 */
434 Notebook.prototype.is_valid_cell_index = function (index) {
541 Notebook.prototype.is_valid_cell_index = function (index) {
435 if (index !== null && index >= 0 && index < this.ncells()) {
542 if (index !== null && index >= 0 && index < this.ncells()) {
436 return true;
543 return true;
437 } else {
544 } else {
438 return false;
545 return false;
439 };
546 };
440 }
547 }
441
548
549 /**
550 * Get the index of the currently selected cell.
551
552 * @method get_selected_index
553 * @return {Number} The selected cell's numeric index
554 */
442 Notebook.prototype.get_selected_index = function () {
555 Notebook.prototype.get_selected_index = function () {
443 var result = null;
556 var result = null;
444 this.get_cell_elements().filter(function (index) {
557 this.get_cell_elements().filter(function (index) {
445 if ($(this).data("cell").selected === true) {
558 if ($(this).data("cell").selected === true) {
446 result = index;
559 result = index;
447 };
560 };
448 });
561 });
449 return result;
562 return result;
450 };
563 };
451
564
452
565
453 // Cell selection.
566 // Cell selection.
454
567
568 /**
569 * Programmatically select a cell.
570 *
571 * @method select
572 * @param {Number} index A cell's index
573 * @return {Notebook} This notebook
574 */
455 Notebook.prototype.select = function (index) {
575 Notebook.prototype.select = function (index) {
456 if (this.is_valid_cell_index(index)) {
576 if (this.is_valid_cell_index(index)) {
457 var sindex = this.get_selected_index()
577 var sindex = this.get_selected_index()
458 if (sindex !== null && index !== sindex) {
578 if (sindex !== null && index !== sindex) {
459 this.get_cell(sindex).unselect();
579 this.get_cell(sindex).unselect();
460 };
580 };
461 var cell = this.get_cell(index);
581 var cell = this.get_cell(index);
462 cell.select();
582 cell.select();
463 if (cell.cell_type === 'heading') {
583 if (cell.cell_type === 'heading') {
464 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
584 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
465 {'cell_type':cell.cell_type,level:cell.level}
585 {'cell_type':cell.cell_type,level:cell.level}
466 );
586 );
467 } else {
587 } else {
468 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
588 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
469 {'cell_type':cell.cell_type}
589 {'cell_type':cell.cell_type}
470 );
590 );
471 };
591 };
472 };
592 };
473 return this;
593 return this;
474 };
594 };
475
595
476
596 /**
597 * Programmatically select the next cell.
598 *
599 * @method select_next
600 * @return {Notebook} This notebook
601 */
477 Notebook.prototype.select_next = function () {
602 Notebook.prototype.select_next = function () {
478 var index = this.get_selected_index();
603 var index = this.get_selected_index();
479 this.select(index+1);
604 this.select(index+1);
480 return this;
605 return this;
481 };
606 };
482
607
483
608 /**
609 * Programmatically select the previous cell.
610 *
611 * @method select_prev
612 * @return {Notebook} This notebook
613 */
484 Notebook.prototype.select_prev = function () {
614 Notebook.prototype.select_prev = function () {
485 var index = this.get_selected_index();
615 var index = this.get_selected_index();
486 this.select(index-1);
616 this.select(index-1);
487 return this;
617 return this;
488 };
618 };
489
619
490
620
491 // Cell movement
621 // Cell movement
492
622
493 /**
623 /**
494 * Move given (or selected) cell up and select it
624 * Move given (or selected) cell up and select it.
625 *
495 * @method move_cell_up
626 * @method move_cell_up
496 * @param [index] {integer} cell index
627 * @param [index] {integer} cell index
628 * @return {Notebook} This notebook
497 **/
629 **/
498 Notebook.prototype.move_cell_up = function (index) {
630 Notebook.prototype.move_cell_up = function (index) {
499 var i = this.index_or_selected(index);
631 var i = this.index_or_selected(index);
500 if (this.is_valid_cell_index(i) && i > 0) {
632 if (this.is_valid_cell_index(i) && i > 0) {
501 var pivot = this.get_cell_element(i-1);
633 var pivot = this.get_cell_element(i-1);
502 var tomove = this.get_cell_element(i);
634 var tomove = this.get_cell_element(i);
503 if (pivot !== null && tomove !== null) {
635 if (pivot !== null && tomove !== null) {
504 tomove.detach();
636 tomove.detach();
505 pivot.before(tomove);
637 pivot.before(tomove);
506 this.select(i-1);
638 this.select(i-1);
507 };
639 };
508 this.dirty = true;
640 this.dirty = true;
509 };
641 };
510 return this;
642 return this;
511 };
643 };
512
644
513
645
514 /**
646 /**
515 * Move given (or selected) cell down and select it
647 * Move given (or selected) cell down and select it
648 *
516 * @method move_cell_down
649 * @method move_cell_down
517 * @param [index] {integer} cell index
650 * @param [index] {integer} cell index
651 * @return {Notebook} This notebook
518 **/
652 **/
519 Notebook.prototype.move_cell_down = function (index) {
653 Notebook.prototype.move_cell_down = function (index) {
520 var i = this.index_or_selected(index);
654 var i = this.index_or_selected(index);
521 if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
655 if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
522 var pivot = this.get_cell_element(i+1);
656 var pivot = this.get_cell_element(i+1);
523 var tomove = this.get_cell_element(i);
657 var tomove = this.get_cell_element(i);
524 if (pivot !== null && tomove !== null) {
658 if (pivot !== null && tomove !== null) {
525 tomove.detach();
659 tomove.detach();
526 pivot.after(tomove);
660 pivot.after(tomove);
527 this.select(i+1);
661 this.select(i+1);
528 };
662 };
529 };
663 };
530 this.dirty = true;
664 this.dirty = true;
531 return this;
665 return this;
532 };
666 };
533
667
534
668
535 // Insertion, deletion.
669 // Insertion, deletion.
536
670
671 /**
672 * Delete a cell from the notebook.
673 *
674 * @method delete_cell
675 * @param [index] A cell's numeric index
676 * @return {Notebook} This notebook
677 */
537 Notebook.prototype.delete_cell = function (index) {
678 Notebook.prototype.delete_cell = function (index) {
538 var i = this.index_or_selected(index);
679 var i = this.index_or_selected(index);
539 var cell = this.get_selected_cell();
680 var cell = this.get_selected_cell();
540 this.undelete_backup = cell.toJSON();
681 this.undelete_backup = cell.toJSON();
541 $('#undelete_cell').removeClass('ui-state-disabled');
682 $('#undelete_cell').removeClass('ui-state-disabled');
542 if (this.is_valid_cell_index(i)) {
683 if (this.is_valid_cell_index(i)) {
543 var ce = this.get_cell_element(i);
684 var ce = this.get_cell_element(i);
544 ce.remove();
685 ce.remove();
545 if (i === (this.ncells())) {
686 if (i === (this.ncells())) {
546 this.select(i-1);
687 this.select(i-1);
547 this.undelete_index = i - 1;
688 this.undelete_index = i - 1;
548 this.undelete_below = true;
689 this.undelete_below = true;
549 } else {
690 } else {
550 this.select(i);
691 this.select(i);
551 this.undelete_index = i;
692 this.undelete_index = i;
552 this.undelete_below = false;
693 this.undelete_below = false;
553 };
694 };
554 this.dirty = true;
695 this.dirty = true;
555 };
696 };
556 return this;
697 return this;
557 };
698 };
558
699
559
560
561
562 /**
700 /**
563 * Insert a cell so that after insertion the cell is at given index.
701 * Insert a cell so that after insertion the cell is at given index.
564 *
702 *
565 * Similar to insert_above, but index parameter is mandatory
703 * Similar to insert_above, but index parameter is mandatory
566 *
704 *
567 * Index will be brought back into the accissible range [0,n]
705 * Index will be brought back into the accissible range [0,n]
568 *
706 *
707 * @method insert_cell_at_index
569 * @param type {string} in ['code','html','markdown','heading']
708 * @param type {string} in ['code','html','markdown','heading']
570 * @param [index] {int} a valid index where to inser cell
709 * @param [index] {int} a valid index where to inser cell
571 *
710 *
572 * @return cell {cell|null} created cell or null
711 * @return cell {cell|null} created cell or null
573 **/
712 **/
574 Notebook.prototype.insert_cell_at_index = function(type, index){
713 Notebook.prototype.insert_cell_at_index = function(type, index){
575
714
576 var ncells = this.ncells();
715 var ncells = this.ncells();
577 var index = Math.min(index,ncells);
716 var index = Math.min(index,ncells);
578 index = Math.max(index,0);
717 index = Math.max(index,0);
579 var cell = null;
718 var cell = null;
580
719
581 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
720 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
582 if (type === 'code') {
721 if (type === 'code') {
583 cell = new IPython.CodeCell(this.kernel);
722 cell = new IPython.CodeCell(this.kernel);
584 cell.set_input_prompt();
723 cell.set_input_prompt();
585 } else if (type === 'markdown') {
724 } else if (type === 'markdown') {
586 cell = new IPython.MarkdownCell();
725 cell = new IPython.MarkdownCell();
587 } else if (type === 'html') {
726 } else if (type === 'html') {
588 cell = new IPython.HTMLCell();
727 cell = new IPython.HTMLCell();
589 } else if (type === 'raw') {
728 } else if (type === 'raw') {
590 cell = new IPython.RawCell();
729 cell = new IPython.RawCell();
591 } else if (type === 'heading') {
730 } else if (type === 'heading') {
592 cell = new IPython.HeadingCell();
731 cell = new IPython.HeadingCell();
593 }
732 }
594
733
595 if(this._insert_element_at_index(cell.element,index)){
734 if(this._insert_element_at_index(cell.element,index)){
596 cell.render();
735 cell.render();
597 this.select(this.find_cell_index(cell));
736 this.select(this.find_cell_index(cell));
598 this.dirty = true;
737 this.dirty = true;
599 }
738 }
600 }
739 }
601 return cell;
740 return cell;
602
741
603 };
742 };
604
743
605 /**
744 /**
606 * Insert an element at given cell index.
745 * Insert an element at given cell index.
607 *
746 *
747 * @method _insert_element_at_index
608 * @param element {dom element} a cell element
748 * @param element {dom element} a cell element
609 * @param [index] {int} a valid index where to inser cell
749 * @param [index] {int} a valid index where to inser cell
610 * @private
750 * @private
611 *
751 *
612 * return true if everything whent fine.
752 * return true if everything whent fine.
613 **/
753 **/
614 Notebook.prototype._insert_element_at_index = function(element, index){
754 Notebook.prototype._insert_element_at_index = function(element, index){
615 if (element === undefined){
755 if (element === undefined){
616 return false;
756 return false;
617 }
757 }
618
758
619 var ncells = this.ncells();
759 var ncells = this.ncells();
620
760
621 if (ncells === 0) {
761 if (ncells === 0) {
622 // special case append if empty
762 // special case append if empty
623 this.element.find('div.end_space').before(element);
763 this.element.find('div.end_space').before(element);
624 } else if ( ncells === index ) {
764 } else if ( ncells === index ) {
625 // special case append it the end, but not empty
765 // special case append it the end, but not empty
626 this.get_cell_element(index-1).after(element);
766 this.get_cell_element(index-1).after(element);
627 } else if (this.is_valid_cell_index(index)) {
767 } else if (this.is_valid_cell_index(index)) {
628 // otherwise always somewhere to append to
768 // otherwise always somewhere to append to
629 this.get_cell_element(index).before(element);
769 this.get_cell_element(index).before(element);
630 } else {
770 } else {
631 return false;
771 return false;
632 }
772 }
633
773
634 if (this.undelete_index !== null && index <= this.undelete_index) {
774 if (this.undelete_index !== null && index <= this.undelete_index) {
635 this.undelete_index = this.undelete_index + 1;
775 this.undelete_index = this.undelete_index + 1;
636 this.dirty = true;
776 this.dirty = true;
637 }
777 }
638 return true;
778 return true;
639 };
779 };
640
780
641 /**
781 /**
642 * Insert a cell of given type above given index, or at top
782 * Insert a cell of given type above given index, or at top
643 * of notebook if index smaller than 0.
783 * of notebook if index smaller than 0.
644 *
784 *
645 * default index value is the one of currently selected cell
785 * default index value is the one of currently selected cell
646 *
786 *
787 * @method insert_cell_above
647 * @param type {string} cell type
788 * @param type {string} cell type
648 * @param [index] {integer}
789 * @param [index] {integer}
649 *
790 *
650 * @return handle to created cell or null
791 * @return handle to created cell or null
651 **/
792 **/
652 Notebook.prototype.insert_cell_above = function (type, index) {
793 Notebook.prototype.insert_cell_above = function (type, index) {
653 index = this.index_or_selected(index);
794 index = this.index_or_selected(index);
654 return this.insert_cell_at_index(type, index);
795 return this.insert_cell_at_index(type, index);
655 };
796 };
656
797
657 /**
798 /**
658 * Insert a cell of given type below given index, or at bottom
799 * Insert a cell of given type below given index, or at bottom
659 * of notebook if index greater thatn number of cell
800 * of notebook if index greater thatn number of cell
660 *
801 *
661 * default index value is the one of currently selected cell
802 * default index value is the one of currently selected cell
662 *
803 *
663 * @method insert_cell_below
804 * @method insert_cell_below
664 * @param type {string} cell type
805 * @param type {string} cell type
665 * @param [index] {integer}
806 * @param [index] {integer}
666 *
807 *
667 * @return handle to created cell or null
808 * @return handle to created cell or null
668 *
809 *
669 **/
810 **/
670 Notebook.prototype.insert_cell_below = function (type, index) {
811 Notebook.prototype.insert_cell_below = function (type, index) {
671 index = this.index_or_selected(index);
812 index = this.index_or_selected(index);
672 return this.insert_cell_at_index(type, index+1);
813 return this.insert_cell_at_index(type, index+1);
673 };
814 };
674
815
675
816
676 /**
817 /**
677 * Insert cell at end of notebook
818 * Insert cell at end of notebook
678 *
819 *
679 * @method insert_cell_at_bottom
820 * @method insert_cell_at_bottom
680 * @param type {String} cell type
821 * @param {String} type cell type
681 *
822 *
682 * @return the added cell; or null
823 * @return the added cell; or null
683 **/
824 **/
684 Notebook.prototype.insert_cell_at_bottom = function (type){
825 Notebook.prototype.insert_cell_at_bottom = function (type){
685 var len = this.ncells();
826 var len = this.ncells();
686 return this.insert_cell_below(type,len-1);
827 return this.insert_cell_below(type,len-1);
687 };
828 };
688
829
689
830 /**
690
831 * Turn a cell into a code cell.
832 *
833 * @method to_code
834 * @param {Number} [index] A cell's index
835 */
691 Notebook.prototype.to_code = function (index) {
836 Notebook.prototype.to_code = function (index) {
692 var i = this.index_or_selected(index);
837 var i = this.index_or_selected(index);
693 if (this.is_valid_cell_index(i)) {
838 if (this.is_valid_cell_index(i)) {
694 var source_element = this.get_cell_element(i);
839 var source_element = this.get_cell_element(i);
695 var source_cell = source_element.data("cell");
840 var source_cell = source_element.data("cell");
696 if (!(source_cell instanceof IPython.CodeCell)) {
841 if (!(source_cell instanceof IPython.CodeCell)) {
697 var target_cell = this.insert_cell_below('code',i);
842 var target_cell = this.insert_cell_below('code',i);
698 var text = source_cell.get_text();
843 var text = source_cell.get_text();
699 if (text === source_cell.placeholder) {
844 if (text === source_cell.placeholder) {
700 text = '';
845 text = '';
701 }
846 }
702 target_cell.set_text(text);
847 target_cell.set_text(text);
703 // make this value the starting point, so that we can only undo
848 // make this value the starting point, so that we can only undo
704 // to this state, instead of a blank cell
849 // to this state, instead of a blank cell
705 target_cell.code_mirror.clearHistory();
850 target_cell.code_mirror.clearHistory();
706 source_element.remove();
851 source_element.remove();
707 this.dirty = true;
852 this.dirty = true;
708 };
853 };
709 };
854 };
710 };
855 };
711
856
712
857 /**
858 * Turn a cell into a Markdown cell.
859 *
860 * @method to_markdown
861 * @param {Number} [index] A cell's index
862 */
713 Notebook.prototype.to_markdown = function (index) {
863 Notebook.prototype.to_markdown = function (index) {
714 var i = this.index_or_selected(index);
864 var i = this.index_or_selected(index);
715 if (this.is_valid_cell_index(i)) {
865 if (this.is_valid_cell_index(i)) {
716 var source_element = this.get_cell_element(i);
866 var source_element = this.get_cell_element(i);
717 var source_cell = source_element.data("cell");
867 var source_cell = source_element.data("cell");
718 if (!(source_cell instanceof IPython.MarkdownCell)) {
868 if (!(source_cell instanceof IPython.MarkdownCell)) {
719 var target_cell = this.insert_cell_below('markdown',i);
869 var target_cell = this.insert_cell_below('markdown',i);
720 var text = source_cell.get_text();
870 var text = source_cell.get_text();
721 if (text === source_cell.placeholder) {
871 if (text === source_cell.placeholder) {
722 text = '';
872 text = '';
723 };
873 };
724 // The edit must come before the set_text.
874 // The edit must come before the set_text.
725 target_cell.edit();
875 target_cell.edit();
726 target_cell.set_text(text);
876 target_cell.set_text(text);
727 // make this value the starting point, so that we can only undo
877 // make this value the starting point, so that we can only undo
728 // to this state, instead of a blank cell
878 // to this state, instead of a blank cell
729 target_cell.code_mirror.clearHistory();
879 target_cell.code_mirror.clearHistory();
730 source_element.remove();
880 source_element.remove();
731 this.dirty = true;
881 this.dirty = true;
732 };
882 };
733 };
883 };
734 };
884 };
735
885
736
886 /**
887 * Turn a cell into an HTML cell.
888 *
889 * @method to_html
890 * @param {Number} [index] A cell's index
891 */
737 Notebook.prototype.to_html = function (index) {
892 Notebook.prototype.to_html = function (index) {
893 // TODO: remove? This is never called
738 var i = this.index_or_selected(index);
894 var i = this.index_or_selected(index);
739 if (this.is_valid_cell_index(i)) {
895 if (this.is_valid_cell_index(i)) {
740 var source_element = this.get_cell_element(i);
896 var source_element = this.get_cell_element(i);
741 var source_cell = source_element.data("cell");
897 var source_cell = source_element.data("cell");
742 var target_cell = null;
898 var target_cell = null;
743 if (!(source_cell instanceof IPython.HTMLCell)) {
899 if (!(source_cell instanceof IPython.HTMLCell)) {
744 target_cell = this.insert_cell_below('html',i);
900 target_cell = this.insert_cell_below('html',i);
745 var text = source_cell.get_text();
901 var text = source_cell.get_text();
746 if (text === source_cell.placeholder) {
902 if (text === source_cell.placeholder) {
747 text = '';
903 text = '';
748 };
904 };
749 // The edit must come before the set_text.
905 // The edit must come before the set_text.
750 target_cell.edit();
906 target_cell.edit();
751 target_cell.set_text(text);
907 target_cell.set_text(text);
752 // make this value the starting point, so that we can only undo
908 // make this value the starting point, so that we can only undo
753 // to this state, instead of a blank cell
909 // to this state, instead of a blank cell
754 target_cell.code_mirror.clearHistory();
910 target_cell.code_mirror.clearHistory();
755 source_element.remove();
911 source_element.remove();
756 this.dirty = true;
912 this.dirty = true;
757 };
913 };
758 };
914 };
759 };
915 };
760
916
761
917 /**
918 * Turn a cell into a raw text cell.
919 *
920 * @method to_raw
921 * @param {Number} [index] A cell's index
922 */
762 Notebook.prototype.to_raw = function (index) {
923 Notebook.prototype.to_raw = function (index) {
763 var i = this.index_or_selected(index);
924 var i = this.index_or_selected(index);
764 if (this.is_valid_cell_index(i)) {
925 if (this.is_valid_cell_index(i)) {
765 var source_element = this.get_cell_element(i);
926 var source_element = this.get_cell_element(i);
766 var source_cell = source_element.data("cell");
927 var source_cell = source_element.data("cell");
767 var target_cell = null;
928 var target_cell = null;
768 if (!(source_cell instanceof IPython.RawCell)) {
929 if (!(source_cell instanceof IPython.RawCell)) {
769 target_cell = this.insert_cell_below('raw',i);
930 target_cell = this.insert_cell_below('raw',i);
770 var text = source_cell.get_text();
931 var text = source_cell.get_text();
771 if (text === source_cell.placeholder) {
932 if (text === source_cell.placeholder) {
772 text = '';
933 text = '';
773 };
934 };
774 // The edit must come before the set_text.
935 // The edit must come before the set_text.
775 target_cell.edit();
936 target_cell.edit();
776 target_cell.set_text(text);
937 target_cell.set_text(text);
777 // make this value the starting point, so that we can only undo
938 // make this value the starting point, so that we can only undo
778 // to this state, instead of a blank cell
939 // to this state, instead of a blank cell
779 target_cell.code_mirror.clearHistory();
940 target_cell.code_mirror.clearHistory();
780 source_element.remove();
941 source_element.remove();
781 this.dirty = true;
942 this.dirty = true;
782 };
943 };
783 };
944 };
784 };
945 };
785
946
786
947 /**
948 * Turn a cell into a heading cell.
949 *
950 * @method to_heading
951 * @param {Number} [index] A cell's index
952 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
953 */
787 Notebook.prototype.to_heading = function (index, level) {
954 Notebook.prototype.to_heading = function (index, level) {
788 level = level || 1;
955 level = level || 1;
789 var i = this.index_or_selected(index);
956 var i = this.index_or_selected(index);
790 if (this.is_valid_cell_index(i)) {
957 if (this.is_valid_cell_index(i)) {
791 var source_element = this.get_cell_element(i);
958 var source_element = this.get_cell_element(i);
792 var source_cell = source_element.data("cell");
959 var source_cell = source_element.data("cell");
793 var target_cell = null;
960 var target_cell = null;
794 if (source_cell instanceof IPython.HeadingCell) {
961 if (source_cell instanceof IPython.HeadingCell) {
795 source_cell.set_level(level);
962 source_cell.set_level(level);
796 } else {
963 } else {
797 target_cell = this.insert_cell_below('heading',i);
964 target_cell = this.insert_cell_below('heading',i);
798 var text = source_cell.get_text();
965 var text = source_cell.get_text();
799 if (text === source_cell.placeholder) {
966 if (text === source_cell.placeholder) {
800 text = '';
967 text = '';
801 };
968 };
802 // The edit must come before the set_text.
969 // The edit must come before the set_text.
803 target_cell.set_level(level);
970 target_cell.set_level(level);
804 target_cell.edit();
971 target_cell.edit();
805 target_cell.set_text(text);
972 target_cell.set_text(text);
806 // make this value the starting point, so that we can only undo
973 // make this value the starting point, so that we can only undo
807 // to this state, instead of a blank cell
974 // to this state, instead of a blank cell
808 target_cell.code_mirror.clearHistory();
975 target_cell.code_mirror.clearHistory();
809 source_element.remove();
976 source_element.remove();
810 this.dirty = true;
977 this.dirty = true;
811 };
978 };
812 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
979 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
813 {'cell_type':'heading',level:level}
980 {'cell_type':'heading',level:level}
814 );
981 );
815 };
982 };
816 };
983 };
817
984
818
985
819 // Cut/Copy/Paste
986 // Cut/Copy/Paste
820
987
988 /**
989 * Enable UI elements for pasting cells.
990 *
991 * @method enable_paste
992 */
821 Notebook.prototype.enable_paste = function () {
993 Notebook.prototype.enable_paste = function () {
822 var that = this;
994 var that = this;
823 if (!this.paste_enabled) {
995 if (!this.paste_enabled) {
824 $('#paste_cell_replace').removeClass('ui-state-disabled')
996 $('#paste_cell_replace').removeClass('ui-state-disabled')
825 .on('click', function () {that.paste_cell_replace();});
997 .on('click', function () {that.paste_cell_replace();});
826 $('#paste_cell_above').removeClass('ui-state-disabled')
998 $('#paste_cell_above').removeClass('ui-state-disabled')
827 .on('click', function () {that.paste_cell_above();});
999 .on('click', function () {that.paste_cell_above();});
828 $('#paste_cell_below').removeClass('ui-state-disabled')
1000 $('#paste_cell_below').removeClass('ui-state-disabled')
829 .on('click', function () {that.paste_cell_below();});
1001 .on('click', function () {that.paste_cell_below();});
830 this.paste_enabled = true;
1002 this.paste_enabled = true;
831 };
1003 };
832 };
1004 };
833
1005
834
1006 /**
1007 * Disable UI elements for pasting cells.
1008 *
1009 * @method disable_paste
1010 */
835 Notebook.prototype.disable_paste = function () {
1011 Notebook.prototype.disable_paste = function () {
836 if (this.paste_enabled) {
1012 if (this.paste_enabled) {
837 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
1013 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
838 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
1014 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
839 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
1015 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
840 this.paste_enabled = false;
1016 this.paste_enabled = false;
841 };
1017 };
842 };
1018 };
843
1019
844
1020 /**
1021 * Cut a cell.
1022 *
1023 * @method cut_cell
1024 */
845 Notebook.prototype.cut_cell = function () {
1025 Notebook.prototype.cut_cell = function () {
846 this.copy_cell();
1026 this.copy_cell();
847 this.delete_cell();
1027 this.delete_cell();
848 }
1028 }
849
1029
1030 /**
1031 * Copy a cell.
1032 *
1033 * @method copy_cell
1034 */
850 Notebook.prototype.copy_cell = function () {
1035 Notebook.prototype.copy_cell = function () {
851 var cell = this.get_selected_cell();
1036 var cell = this.get_selected_cell();
852 this.clipboard = cell.toJSON();
1037 this.clipboard = cell.toJSON();
853 this.enable_paste();
1038 this.enable_paste();
854 };
1039 };
855
1040
856
1041 /**
1042 * Replace the selected cell with a cell in the clipboard.
1043 *
1044 * @method paste_cell_replace
1045 */
857 Notebook.prototype.paste_cell_replace = function () {
1046 Notebook.prototype.paste_cell_replace = function () {
858 if (this.clipboard !== null && this.paste_enabled) {
1047 if (this.clipboard !== null && this.paste_enabled) {
859 var cell_data = this.clipboard;
1048 var cell_data = this.clipboard;
860 var new_cell = this.insert_cell_above(cell_data.cell_type);
1049 var new_cell = this.insert_cell_above(cell_data.cell_type);
861 new_cell.fromJSON(cell_data);
1050 new_cell.fromJSON(cell_data);
862 var old_cell = this.get_next_cell(new_cell);
1051 var old_cell = this.get_next_cell(new_cell);
863 this.delete_cell(this.find_cell_index(old_cell));
1052 this.delete_cell(this.find_cell_index(old_cell));
864 this.select(this.find_cell_index(new_cell));
1053 this.select(this.find_cell_index(new_cell));
865 };
1054 };
866 };
1055 };
867
1056
868
1057 /**
1058 * Paste a cell from the clipboard above the selected cell.
1059 *
1060 * @method paste_cell_above
1061 */
869 Notebook.prototype.paste_cell_above = function () {
1062 Notebook.prototype.paste_cell_above = function () {
870 if (this.clipboard !== null && this.paste_enabled) {
1063 if (this.clipboard !== null && this.paste_enabled) {
871 var cell_data = this.clipboard;
1064 var cell_data = this.clipboard;
872 var new_cell = this.insert_cell_above(cell_data.cell_type);
1065 var new_cell = this.insert_cell_above(cell_data.cell_type);
873 new_cell.fromJSON(cell_data);
1066 new_cell.fromJSON(cell_data);
874 };
1067 };
875 };
1068 };
876
1069
877
1070 /**
1071 * Paste a cell from the clipboard below the selected cell.
1072 *
1073 * @method paste_cell_below
1074 */
878 Notebook.prototype.paste_cell_below = function () {
1075 Notebook.prototype.paste_cell_below = function () {
879 if (this.clipboard !== null && this.paste_enabled) {
1076 if (this.clipboard !== null && this.paste_enabled) {
880 var cell_data = this.clipboard;
1077 var cell_data = this.clipboard;
881 var new_cell = this.insert_cell_below(cell_data.cell_type);
1078 var new_cell = this.insert_cell_below(cell_data.cell_type);
882 new_cell.fromJSON(cell_data);
1079 new_cell.fromJSON(cell_data);
883 };
1080 };
884 };
1081 };
885
1082
886 // Cell undelete
1083 // Cell undelete
887
1084
1085 /**
1086 * Restore the most recently deleted cell.
1087 *
1088 * @method undelete
1089 */
888 Notebook.prototype.undelete = function() {
1090 Notebook.prototype.undelete = function() {
889 if (this.undelete_backup !== null && this.undelete_index !== null) {
1091 if (this.undelete_backup !== null && this.undelete_index !== null) {
890 var current_index = this.get_selected_index();
1092 var current_index = this.get_selected_index();
891 if (this.undelete_index < current_index) {
1093 if (this.undelete_index < current_index) {
892 current_index = current_index + 1;
1094 current_index = current_index + 1;
893 }
1095 }
894 if (this.undelete_index >= this.ncells()) {
1096 if (this.undelete_index >= this.ncells()) {
895 this.select(this.ncells() - 1);
1097 this.select(this.ncells() - 1);
896 }
1098 }
897 else {
1099 else {
898 this.select(this.undelete_index);
1100 this.select(this.undelete_index);
899 }
1101 }
900 var cell_data = this.undelete_backup;
1102 var cell_data = this.undelete_backup;
901 var new_cell = null;
1103 var new_cell = null;
902 if (this.undelete_below) {
1104 if (this.undelete_below) {
903 new_cell = this.insert_cell_below(cell_data.cell_type);
1105 new_cell = this.insert_cell_below(cell_data.cell_type);
904 } else {
1106 } else {
905 new_cell = this.insert_cell_above(cell_data.cell_type);
1107 new_cell = this.insert_cell_above(cell_data.cell_type);
906 }
1108 }
907 new_cell.fromJSON(cell_data);
1109 new_cell.fromJSON(cell_data);
908 this.select(current_index);
1110 this.select(current_index);
909 this.undelete_backup = null;
1111 this.undelete_backup = null;
910 this.undelete_index = null;
1112 this.undelete_index = null;
911 }
1113 }
912 $('#undelete_cell').addClass('ui-state-disabled');
1114 $('#undelete_cell').addClass('ui-state-disabled');
913 }
1115 }
914
1116
915 // Split/merge
1117 // Split/merge
916
1118
1119 /**
1120 * Split the selected cell into two, at the cursor.
1121 *
1122 * @method split_cell
1123 */
917 Notebook.prototype.split_cell = function () {
1124 Notebook.prototype.split_cell = function () {
918 // Todo: implement spliting for other cell types.
1125 // Todo: implement spliting for other cell types.
919 var cell = this.get_selected_cell();
1126 var cell = this.get_selected_cell();
920 if (cell.is_splittable()) {
1127 if (cell.is_splittable()) {
921 var texta = cell.get_pre_cursor();
1128 var texta = cell.get_pre_cursor();
922 var textb = cell.get_post_cursor();
1129 var textb = cell.get_post_cursor();
923 if (cell instanceof IPython.CodeCell) {
1130 if (cell instanceof IPython.CodeCell) {
924 cell.set_text(texta);
1131 cell.set_text(texta);
925 var new_cell = this.insert_cell_below('code');
1132 var new_cell = this.insert_cell_below('code');
926 new_cell.set_text(textb);
1133 new_cell.set_text(textb);
927 } else if (cell instanceof IPython.MarkdownCell) {
1134 } else if (cell instanceof IPython.MarkdownCell) {
928 cell.set_text(texta);
1135 cell.set_text(texta);
929 cell.render();
1136 cell.render();
930 var new_cell = this.insert_cell_below('markdown');
1137 var new_cell = this.insert_cell_below('markdown');
931 new_cell.edit(); // editor must be visible to call set_text
1138 new_cell.edit(); // editor must be visible to call set_text
932 new_cell.set_text(textb);
1139 new_cell.set_text(textb);
933 new_cell.render();
1140 new_cell.render();
934 } else if (cell instanceof IPython.HTMLCell) {
1141 } else if (cell instanceof IPython.HTMLCell) {
935 cell.set_text(texta);
1142 cell.set_text(texta);
936 cell.render();
1143 cell.render();
937 var new_cell = this.insert_cell_below('html');
1144 var new_cell = this.insert_cell_below('html');
938 new_cell.edit(); // editor must be visible to call set_text
1145 new_cell.edit(); // editor must be visible to call set_text
939 new_cell.set_text(textb);
1146 new_cell.set_text(textb);
940 new_cell.render();
1147 new_cell.render();
941 };
1148 };
942 };
1149 };
943 };
1150 };
944
1151
945
1152 /**
1153 * Combine the selected cell into the cell above it.
1154 *
1155 * @method merge_cell_above
1156 */
946 Notebook.prototype.merge_cell_above = function () {
1157 Notebook.prototype.merge_cell_above = function () {
947 var index = this.get_selected_index();
1158 var index = this.get_selected_index();
948 var cell = this.get_cell(index);
1159 var cell = this.get_cell(index);
949 if (index > 0) {
1160 if (index > 0) {
950 var upper_cell = this.get_cell(index-1);
1161 var upper_cell = this.get_cell(index-1);
951 var upper_text = upper_cell.get_text();
1162 var upper_text = upper_cell.get_text();
952 var text = cell.get_text();
1163 var text = cell.get_text();
953 if (cell instanceof IPython.CodeCell) {
1164 if (cell instanceof IPython.CodeCell) {
954 cell.set_text(upper_text+'\n'+text);
1165 cell.set_text(upper_text+'\n'+text);
955 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
1166 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
956 cell.edit();
1167 cell.edit();
957 cell.set_text(upper_text+'\n'+text);
1168 cell.set_text(upper_text+'\n'+text);
958 cell.render();
1169 cell.render();
959 };
1170 };
960 this.delete_cell(index-1);
1171 this.delete_cell(index-1);
961 this.select(this.find_cell_index(cell));
1172 this.select(this.find_cell_index(cell));
962 };
1173 };
963 };
1174 };
964
1175
965
1176 /**
1177 * Combine the selected cell into the cell below it.
1178 *
1179 * @method merge_cell_below
1180 */
966 Notebook.prototype.merge_cell_below = function () {
1181 Notebook.prototype.merge_cell_below = function () {
967 var index = this.get_selected_index();
1182 var index = this.get_selected_index();
968 var cell = this.get_cell(index);
1183 var cell = this.get_cell(index);
969 if (index < this.ncells()-1) {
1184 if (index < this.ncells()-1) {
970 var lower_cell = this.get_cell(index+1);
1185 var lower_cell = this.get_cell(index+1);
971 var lower_text = lower_cell.get_text();
1186 var lower_text = lower_cell.get_text();
972 var text = cell.get_text();
1187 var text = cell.get_text();
973 if (cell instanceof IPython.CodeCell) {
1188 if (cell instanceof IPython.CodeCell) {
974 cell.set_text(text+'\n'+lower_text);
1189 cell.set_text(text+'\n'+lower_text);
975 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
1190 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
976 cell.edit();
1191 cell.edit();
977 cell.set_text(text+'\n'+lower_text);
1192 cell.set_text(text+'\n'+lower_text);
978 cell.render();
1193 cell.render();
979 };
1194 };
980 this.delete_cell(index+1);
1195 this.delete_cell(index+1);
981 this.select(this.find_cell_index(cell));
1196 this.select(this.find_cell_index(cell));
982 };
1197 };
983 };
1198 };
984
1199
985
1200
986 // Cell collapsing and output clearing
1201 // Cell collapsing and output clearing
987
1202
1203 /**
1204 * Hide a cell's output.
1205 *
1206 * @method collapse
1207 * @param {Number} index A cell's numeric index
1208 */
988 Notebook.prototype.collapse = function (index) {
1209 Notebook.prototype.collapse = function (index) {
989 var i = this.index_or_selected(index);
1210 var i = this.index_or_selected(index);
990 this.get_cell(i).collapse();
1211 this.get_cell(i).collapse();
991 this.dirty = true;
1212 this.dirty = true;
992 };
1213 };
993
1214
994
1215 /**
1216 * Show a cell's output.
1217 *
1218 * @method expand
1219 * @param {Number} index A cell's numeric index
1220 */
995 Notebook.prototype.expand = function (index) {
1221 Notebook.prototype.expand = function (index) {
996 var i = this.index_or_selected(index);
1222 var i = this.index_or_selected(index);
997 this.get_cell(i).expand();
1223 this.get_cell(i).expand();
998 this.dirty = true;
1224 this.dirty = true;
999 };
1225 };
1000
1226
1001
1227 /** Toggle whether a cell's output is collapsed or expanded.
1228 *
1229 * @method toggle_output
1230 * @param {Number} index A cell's numeric index
1231 */
1002 Notebook.prototype.toggle_output = function (index) {
1232 Notebook.prototype.toggle_output = function (index) {
1003 var i = this.index_or_selected(index);
1233 var i = this.index_or_selected(index);
1004 this.get_cell(i).toggle_output();
1234 this.get_cell(i).toggle_output();
1005 this.dirty = true;
1235 this.dirty = true;
1006 };
1236 };
1007
1237
1008
1238 /**
1239 * Toggle a scrollbar for long cell outputs.
1240 *
1241 * @method toggle_output_scroll
1242 * @param {Number} index A cell's numeric index
1243 */
1009 Notebook.prototype.toggle_output_scroll = function (index) {
1244 Notebook.prototype.toggle_output_scroll = function (index) {
1010 var i = this.index_or_selected(index);
1245 var i = this.index_or_selected(index);
1011 this.get_cell(i).toggle_output_scroll();
1246 this.get_cell(i).toggle_output_scroll();
1012 };
1247 };
1013
1248
1014
1249 /**
1250 * Hide each code cell's output area.
1251 *
1252 * @method collapse_all_output
1253 */
1015 Notebook.prototype.collapse_all_output = function () {
1254 Notebook.prototype.collapse_all_output = function () {
1016 var ncells = this.ncells();
1255 var ncells = this.ncells();
1017 var cells = this.get_cells();
1256 var cells = this.get_cells();
1018 for (var i=0; i<ncells; i++) {
1257 for (var i=0; i<ncells; i++) {
1019 if (cells[i] instanceof IPython.CodeCell) {
1258 if (cells[i] instanceof IPython.CodeCell) {
1020 cells[i].output_area.collapse();
1259 cells[i].output_area.collapse();
1021 }
1260 }
1022 };
1261 };
1023 // this should not be set if the `collapse` key is removed from nbformat
1262 // this should not be set if the `collapse` key is removed from nbformat
1024 this.dirty = true;
1263 this.dirty = true;
1025 };
1264 };
1026
1265
1027
1266 /**
1267 * Expand each code cell's output area, and add a scrollbar for long output.
1268 *
1269 * @method scroll_all_output
1270 */
1028 Notebook.prototype.scroll_all_output = function () {
1271 Notebook.prototype.scroll_all_output = function () {
1029 var ncells = this.ncells();
1272 var ncells = this.ncells();
1030 var cells = this.get_cells();
1273 var cells = this.get_cells();
1031 for (var i=0; i<ncells; i++) {
1274 for (var i=0; i<ncells; i++) {
1032 if (cells[i] instanceof IPython.CodeCell) {
1275 if (cells[i] instanceof IPython.CodeCell) {
1033 cells[i].output_area.expand();
1276 cells[i].output_area.expand();
1034 cells[i].output_area.scroll_if_long(20);
1277 cells[i].output_area.scroll_if_long(20);
1035 }
1278 }
1036 };
1279 };
1037 // this should not be set if the `collapse` key is removed from nbformat
1280 // this should not be set if the `collapse` key is removed from nbformat
1038 this.dirty = true;
1281 this.dirty = true;
1039 };
1282 };
1040
1283
1041
1284 /**
1285 * Expand each code cell's output area, and remove scrollbars.
1286 *
1287 * @method expand_all_output
1288 */
1042 Notebook.prototype.expand_all_output = function () {
1289 Notebook.prototype.expand_all_output = function () {
1043 var ncells = this.ncells();
1290 var ncells = this.ncells();
1044 var cells = this.get_cells();
1291 var cells = this.get_cells();
1045 for (var i=0; i<ncells; i++) {
1292 for (var i=0; i<ncells; i++) {
1046 if (cells[i] instanceof IPython.CodeCell) {
1293 if (cells[i] instanceof IPython.CodeCell) {
1047 cells[i].output_area.expand();
1294 cells[i].output_area.expand();
1048 cells[i].output_area.unscroll_area();
1295 cells[i].output_area.unscroll_area();
1049 }
1296 }
1050 };
1297 };
1051 // this should not be set if the `collapse` key is removed from nbformat
1298 // this should not be set if the `collapse` key is removed from nbformat
1052 this.dirty = true;
1299 this.dirty = true;
1053 };
1300 };
1054
1301
1055
1302 /**
1303 * Clear each code cell's output area.
1304 *
1305 * @method clear_all_output
1306 */
1056 Notebook.prototype.clear_all_output = function () {
1307 Notebook.prototype.clear_all_output = function () {
1057 var ncells = this.ncells();
1308 var ncells = this.ncells();
1058 var cells = this.get_cells();
1309 var cells = this.get_cells();
1059 for (var i=0; i<ncells; i++) {
1310 for (var i=0; i<ncells; i++) {
1060 if (cells[i] instanceof IPython.CodeCell) {
1311 if (cells[i] instanceof IPython.CodeCell) {
1061 cells[i].clear_output(true,true,true);
1312 cells[i].clear_output(true,true,true);
1062 // Make all In[] prompts blank, as well
1313 // Make all In[] prompts blank, as well
1063 // TODO: make this configurable (via checkbox?)
1314 // TODO: make this configurable (via checkbox?)
1064 cells[i].set_input_prompt();
1315 cells[i].set_input_prompt();
1065 }
1316 }
1066 };
1317 };
1067 this.dirty = true;
1318 this.dirty = true;
1068 };
1319 };
1069
1320
1070
1321
1071 // Other cell functions: line numbers, ...
1322 // Other cell functions: line numbers, ...
1072
1323
1324 /**
1325 * Toggle line numbers in the selected cell's input area.
1326 *
1327 * @method cell_toggle_line_numbers
1328 */
1073 Notebook.prototype.cell_toggle_line_numbers = function() {
1329 Notebook.prototype.cell_toggle_line_numbers = function() {
1074 this.get_selected_cell().toggle_line_numbers();
1330 this.get_selected_cell().toggle_line_numbers();
1075 };
1331 };
1076
1332
1077 // Kernel related things
1333 // Kernel related things
1078
1334
1335 /**
1336 * Start a new kernel and set it on each code cell.
1337 *
1338 * @method start_kernel
1339 */
1079 Notebook.prototype.start_kernel = function () {
1340 Notebook.prototype.start_kernel = function () {
1080 var base_url = $('body').data('baseKernelUrl') + "kernels";
1341 var base_url = $('body').data('baseKernelUrl') + "kernels";
1081 this.kernel = new IPython.Kernel(base_url);
1342 this.kernel = new IPython.Kernel(base_url);
1082 this.kernel.start(this.notebook_id);
1343 this.kernel.start(this.notebook_id);
1083 // Now that the kernel has been created, tell the CodeCells about it.
1344 // Now that the kernel has been created, tell the CodeCells about it.
1084 var ncells = this.ncells();
1345 var ncells = this.ncells();
1085 for (var i=0; i<ncells; i++) {
1346 for (var i=0; i<ncells; i++) {
1086 var cell = this.get_cell(i);
1347 var cell = this.get_cell(i);
1087 if (cell instanceof IPython.CodeCell) {
1348 if (cell instanceof IPython.CodeCell) {
1088 cell.set_kernel(this.kernel)
1349 cell.set_kernel(this.kernel)
1089 };
1350 };
1090 };
1351 };
1091 };
1352 };
1092
1353
1093
1354 /**
1355 * Prompt the user to restart the IPython kernel.
1356 *
1357 * @method restart_kernel
1358 */
1094 Notebook.prototype.restart_kernel = function () {
1359 Notebook.prototype.restart_kernel = function () {
1095 var that = this;
1360 var that = this;
1096 var dialog = $('<div/>');
1361 var dialog = $('<div/>');
1097 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1362 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1098 $(document).append(dialog);
1363 $(document).append(dialog);
1099 dialog.dialog({
1364 dialog.dialog({
1100 resizable: false,
1365 resizable: false,
1101 modal: true,
1366 modal: true,
1102 title: "Restart kernel or continue running?",
1367 title: "Restart kernel or continue running?",
1103 closeText: '',
1368 closeText: '',
1104 buttons : {
1369 buttons : {
1105 "Restart": function () {
1370 "Restart": function () {
1106 that.kernel.restart();
1371 that.kernel.restart();
1107 $(this).dialog('close');
1372 $(this).dialog('close');
1108 },
1373 },
1109 "Continue running": function () {
1374 "Continue running": function () {
1110 $(this).dialog('close');
1375 $(this).dialog('close');
1111 }
1376 }
1112 }
1377 }
1113 });
1378 });
1114 };
1379 };
1115
1380
1116
1381 /**
1382 * Run the selected cell.
1383 *
1384 * This executes code cells, and skips all others.
1385 *
1386 * @method execute_selected_cell
1387 * @param {Object} options Customize post-execution behavior
1388 */
1117 Notebook.prototype.execute_selected_cell = function (options) {
1389 Notebook.prototype.execute_selected_cell = function (options) {
1118 // add_new: should a new cell be added if we are at the end of the nb
1390 // add_new: should a new cell be added if we are at the end of the nb
1119 // terminal: execute in terminal mode, which stays in the current cell
1391 // terminal: execute in terminal mode, which stays in the current cell
1120 var default_options = {terminal: false, add_new: true};
1392 var default_options = {terminal: false, add_new: true};
1121 $.extend(default_options, options);
1393 $.extend(default_options, options);
1122 var that = this;
1394 var that = this;
1123 var cell = that.get_selected_cell();
1395 var cell = that.get_selected_cell();
1124 var cell_index = that.find_cell_index(cell);
1396 var cell_index = that.find_cell_index(cell);
1125 if (cell instanceof IPython.CodeCell) {
1397 if (cell instanceof IPython.CodeCell) {
1126 cell.execute();
1398 cell.execute();
1127 } else if (cell instanceof IPython.HTMLCell) {
1399 } else if (cell instanceof IPython.HTMLCell) {
1128 cell.render();
1400 cell.render();
1129 }
1401 }
1130 if (default_options.terminal) {
1402 if (default_options.terminal) {
1131 cell.select_all();
1403 cell.select_all();
1132 } else {
1404 } else {
1133 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1405 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1134 that.insert_cell_below('code');
1406 that.insert_cell_below('code');
1135 // If we are adding a new cell at the end, scroll down to show it.
1407 // If we are adding a new cell at the end, scroll down to show it.
1136 that.scroll_to_bottom();
1408 that.scroll_to_bottom();
1137 } else {
1409 } else {
1138 that.select(cell_index+1);
1410 that.select(cell_index+1);
1139 };
1411 };
1140 };
1412 };
1141 this.dirty = true;
1413 this.dirty = true;
1142 };
1414 };
1143
1415
1144
1416 /**
1417 * Execute all cells below the selected cell.
1418 *
1419 * @method execute_cells_below
1420 */
1145 Notebook.prototype.execute_cells_below = function () {
1421 Notebook.prototype.execute_cells_below = function () {
1146 this.execute_cell_range(this.get_selected_index(), this.ncells());
1422 this.execute_cell_range(this.get_selected_index(), this.ncells());
1147 this.scroll_to_bottom();
1423 this.scroll_to_bottom();
1148 };
1424 };
1149
1425
1426 /**
1427 * Execute all cells above the selected cell.
1428 *
1429 * @method execute_cells_above
1430 */
1150 Notebook.prototype.execute_cells_above = function () {
1431 Notebook.prototype.execute_cells_above = function () {
1151 this.execute_cell_range(0, this.get_selected_index());
1432 this.execute_cell_range(0, this.get_selected_index());
1152 };
1433 };
1153
1434
1435 /**
1436 * Execute all cells.
1437 *
1438 * @method execute_all_cells
1439 */
1154 Notebook.prototype.execute_all_cells = function () {
1440 Notebook.prototype.execute_all_cells = function () {
1155 this.execute_cell_range(0, this.ncells());
1441 this.execute_cell_range(0, this.ncells());
1156 this.scroll_to_bottom();
1442 this.scroll_to_bottom();
1157 };
1443 };
1158
1444
1445 /**
1446 * Execute a contiguous range of cells.
1447 *
1448 * @method execute_cell_range
1449 * @param {Number} start Index of the first cell to execute (inclusive)
1450 * @param {Number} end Index of the last cell to execute (exclusive)
1451 */
1159 Notebook.prototype.execute_cell_range = function (start, end) {
1452 Notebook.prototype.execute_cell_range = function (start, end) {
1160 for (var i=start; i<end; i++) {
1453 for (var i=start; i<end; i++) {
1161 this.select(i);
1454 this.select(i);
1162 this.execute_selected_cell({add_new:false});
1455 this.execute_selected_cell({add_new:false});
1163 };
1456 };
1164 };
1457 };
1165
1458
1166 // Persistance and loading
1459 // Persistance and loading
1167
1460
1461 /**
1462 * Getter method for this notebook's ID.
1463 *
1464 * @method get_notebook_id
1465 * @return {String} This notebook's ID
1466 */
1168 Notebook.prototype.get_notebook_id = function () {
1467 Notebook.prototype.get_notebook_id = function () {
1169 return this.notebook_id;
1468 return this.notebook_id;
1170 };
1469 };
1171
1470
1172
1471 /**
1472 * Getter method for this notebook's name.
1473 *
1474 * @method get_notebook_name
1475 * @return {String} This notebook's name
1476 */
1173 Notebook.prototype.get_notebook_name = function () {
1477 Notebook.prototype.get_notebook_name = function () {
1174 return this.notebook_name;
1478 return this.notebook_name;
1175 };
1479 };
1176
1480
1177
1481 /**
1482 * Setter method for this notebook's name.
1483 *
1484 * @method set_notebook_name
1485 * @param {String} name A new name for this notebook
1486 */
1178 Notebook.prototype.set_notebook_name = function (name) {
1487 Notebook.prototype.set_notebook_name = function (name) {
1179 this.notebook_name = name;
1488 this.notebook_name = name;
1180 };
1489 };
1181
1490
1182
1491 /**
1492 * Check that a notebook's name is valid.
1493 *
1494 * @method test_notebook_name
1495 * @param {String} nbname A name for this notebook
1496 * @return {Boolean} True if the name is valid, false if invalid
1497 */
1183 Notebook.prototype.test_notebook_name = function (nbname) {
1498 Notebook.prototype.test_notebook_name = function (nbname) {
1184 nbname = nbname || '';
1499 nbname = nbname || '';
1185 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1500 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1186 return true;
1501 return true;
1187 } else {
1502 } else {
1188 return false;
1503 return false;
1189 };
1504 };
1190 };
1505 };
1191
1506
1192
1507 /**
1508 * Load a notebook from JSON (.ipynb).
1509 *
1510 * This currently handles one worksheet: others are deleted.
1511 *
1512 * @method fromJSON
1513 * @param {Object} data JSON representation of a notebook
1514 */
1193 Notebook.prototype.fromJSON = function (data) {
1515 Notebook.prototype.fromJSON = function (data) {
1194 var ncells = this.ncells();
1516 var ncells = this.ncells();
1195 var i;
1517 var i;
1196 for (i=0; i<ncells; i++) {
1518 for (i=0; i<ncells; i++) {
1197 // Always delete cell 0 as they get renumbered as they are deleted.
1519 // Always delete cell 0 as they get renumbered as they are deleted.
1198 this.delete_cell(0);
1520 this.delete_cell(0);
1199 };
1521 };
1200 // Save the metadata and name.
1522 // Save the metadata and name.
1201 this.metadata = data.metadata;
1523 this.metadata = data.metadata;
1202 this.notebook_name = data.metadata.name;
1524 this.notebook_name = data.metadata.name;
1203 // Only handle 1 worksheet for now.
1525 // Only handle 1 worksheet for now.
1204 var worksheet = data.worksheets[0];
1526 var worksheet = data.worksheets[0];
1205 if (worksheet !== undefined) {
1527 if (worksheet !== undefined) {
1206 if (worksheet.metadata) {
1528 if (worksheet.metadata) {
1207 this.worksheet_metadata = worksheet.metadata;
1529 this.worksheet_metadata = worksheet.metadata;
1208 }
1530 }
1209 var new_cells = worksheet.cells;
1531 var new_cells = worksheet.cells;
1210 ncells = new_cells.length;
1532 ncells = new_cells.length;
1211 var cell_data = null;
1533 var cell_data = null;
1212 var new_cell = null;
1534 var new_cell = null;
1213 for (i=0; i<ncells; i++) {
1535 for (i=0; i<ncells; i++) {
1214 cell_data = new_cells[i];
1536 cell_data = new_cells[i];
1215 // VERSIONHACK: plaintext -> raw
1537 // VERSIONHACK: plaintext -> raw
1216 // handle never-released plaintext name for raw cells
1538 // handle never-released plaintext name for raw cells
1217 if (cell_data.cell_type === 'plaintext'){
1539 if (cell_data.cell_type === 'plaintext'){
1218 cell_data.cell_type = 'raw';
1540 cell_data.cell_type = 'raw';
1219 }
1541 }
1220
1542
1221 new_cell = this.insert_cell_below(cell_data.cell_type);
1543 new_cell = this.insert_cell_below(cell_data.cell_type);
1222 new_cell.fromJSON(cell_data);
1544 new_cell.fromJSON(cell_data);
1223 };
1545 };
1224 };
1546 };
1225 if (data.worksheets.length > 1) {
1547 if (data.worksheets.length > 1) {
1226 var dialog = $('<div/>');
1548 var dialog = $('<div/>');
1227 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1549 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1228 "but this version of IPython can only handle the first. " +
1550 "but this version of IPython can only handle the first. " +
1229 "If you save this notebook, worksheets after the first will be lost."
1551 "If you save this notebook, worksheets after the first will be lost."
1230 );
1552 );
1231 this.element.append(dialog);
1553 this.element.append(dialog);
1232 dialog.dialog({
1554 dialog.dialog({
1233 resizable: false,
1555 resizable: false,
1234 modal: true,
1556 modal: true,
1235 title: "Multiple worksheets",
1557 title: "Multiple worksheets",
1236 closeText: "",
1558 closeText: "",
1237 close: function(event, ui) {$(this).dialog('destroy').remove();},
1559 close: function(event, ui) {$(this).dialog('destroy').remove();},
1238 buttons : {
1560 buttons : {
1239 "OK": function () {
1561 "OK": function () {
1240 $(this).dialog('close');
1562 $(this).dialog('close');
1241 }
1563 }
1242 },
1564 },
1243 width: 400
1565 width: 400
1244 });
1566 });
1245 }
1567 }
1246 };
1568 };
1247
1569
1248
1570 /**
1571 * Dump this notebook into a JSON-friendly object.
1572 *
1573 * @method toJSON
1574 * @return {Object} A JSON-friendly representation of this notebook.
1575 */
1249 Notebook.prototype.toJSON = function () {
1576 Notebook.prototype.toJSON = function () {
1250 var cells = this.get_cells();
1577 var cells = this.get_cells();
1251 var ncells = cells.length;
1578 var ncells = cells.length;
1252 var cell_array = new Array(ncells);
1579 var cell_array = new Array(ncells);
1253 for (var i=0; i<ncells; i++) {
1580 for (var i=0; i<ncells; i++) {
1254 cell_array[i] = cells[i].toJSON();
1581 cell_array[i] = cells[i].toJSON();
1255 };
1582 };
1256 var data = {
1583 var data = {
1257 // Only handle 1 worksheet for now.
1584 // Only handle 1 worksheet for now.
1258 worksheets : [{
1585 worksheets : [{
1259 cells: cell_array,
1586 cells: cell_array,
1260 metadata: this.worksheet_metadata
1587 metadata: this.worksheet_metadata
1261 }],
1588 }],
1262 metadata : this.metadata
1589 metadata : this.metadata
1263 };
1590 };
1264 return data;
1591 return data;
1265 };
1592 };
1266
1593
1594 /**
1595 * Save this notebook on the server.
1596 *
1597 * @method save_notebook
1598 */
1267 Notebook.prototype.save_notebook = function () {
1599 Notebook.prototype.save_notebook = function () {
1268 // We may want to move the name/id/nbformat logic inside toJSON?
1600 // We may want to move the name/id/nbformat logic inside toJSON?
1269 var data = this.toJSON();
1601 var data = this.toJSON();
1270 data.metadata.name = this.notebook_name;
1602 data.metadata.name = this.notebook_name;
1271 data.nbformat = this.nbformat;
1603 data.nbformat = this.nbformat;
1272 data.nbformat_minor = this.nbformat_minor;
1604 data.nbformat_minor = this.nbformat_minor;
1273 // We do the call with settings so we can set cache to false.
1605 // We do the call with settings so we can set cache to false.
1274 var settings = {
1606 var settings = {
1275 processData : false,
1607 processData : false,
1276 cache : false,
1608 cache : false,
1277 type : "PUT",
1609 type : "PUT",
1278 data : JSON.stringify(data),
1610 data : JSON.stringify(data),
1279 headers : {'Content-Type': 'application/json'},
1611 headers : {'Content-Type': 'application/json'},
1280 success : $.proxy(this.save_notebook_success,this),
1612 success : $.proxy(this.save_notebook_success,this),
1281 error : $.proxy(this.save_notebook_error,this)
1613 error : $.proxy(this.save_notebook_error,this)
1282 };
1614 };
1283 $([IPython.events]).trigger('notebook_saving.Notebook');
1615 $([IPython.events]).trigger('notebook_saving.Notebook');
1284 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1616 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1285 $.ajax(url, settings);
1617 $.ajax(url, settings);
1286 };
1618 };
1287
1619
1288
1620 /**
1621 * Success callback for saving a notebook.
1622 *
1623 * @method save_notebook_success
1624 * @param {Object} data JSON representation of a notebook
1625 * @param {String} status Description of response status
1626 * @param {jqXHR} xhr jQuery Ajax object
1627 */
1289 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1628 Notebook.prototype.save_notebook_success = function (data, status, xhr) {
1290 this.dirty = false;
1629 this.dirty = false;
1291 $([IPython.events]).trigger('notebook_saved.Notebook');
1630 $([IPython.events]).trigger('notebook_saved.Notebook');
1292 };
1631 };
1293
1632
1294
1633 /**
1634 * Failure callback for saving a notebook.
1635 *
1636 * @method save_notebook_error
1637 * @param {jqXHR} xhr jQuery Ajax object
1638 * @param {String} status Description of response status
1639 * @param {String} error_msg HTTP error message
1640 */
1295 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1641 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1296 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1642 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1297 };
1643 };
1298
1644
1299
1645 /**
1646 * Request a notebook's data from the server.
1647 *
1648 * @method load_notebook
1649 * @param {String} notebook_id A notebook to load
1650 */
1300 Notebook.prototype.load_notebook = function (notebook_id) {
1651 Notebook.prototype.load_notebook = function (notebook_id) {
1301 var that = this;
1652 var that = this;
1302 this.notebook_id = notebook_id;
1653 this.notebook_id = notebook_id;
1303 // We do the call with settings so we can set cache to false.
1654 // We do the call with settings so we can set cache to false.
1304 var settings = {
1655 var settings = {
1305 processData : false,
1656 processData : false,
1306 cache : false,
1657 cache : false,
1307 type : "GET",
1658 type : "GET",
1308 dataType : "json",
1659 dataType : "json",
1309 success : $.proxy(this.load_notebook_success,this),
1660 success : $.proxy(this.load_notebook_success,this),
1310 error : $.proxy(this.load_notebook_error,this),
1661 error : $.proxy(this.load_notebook_error,this),
1311 };
1662 };
1312 $([IPython.events]).trigger('notebook_loading.Notebook');
1663 $([IPython.events]).trigger('notebook_loading.Notebook');
1313 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1664 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1314 $.ajax(url, settings);
1665 $.ajax(url, settings);
1315 };
1666 };
1316
1667
1317
1668 /**
1669 * Success callback for loading a notebook from the server.
1670 *
1671 * Load notebook data from the JSON response.
1672 *
1673 * @method load_notebook_success
1674 * @param {Object} data JSON representation of a notebook
1675 * @param {String} status Description of response status
1676 * @param {jqXHR} xhr jQuery Ajax object
1677 */
1318 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1678 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1319 this.fromJSON(data);
1679 this.fromJSON(data);
1320 if (this.ncells() === 0) {
1680 if (this.ncells() === 0) {
1321 this.insert_cell_below('code');
1681 this.insert_cell_below('code');
1322 };
1682 };
1323 this.dirty = false;
1683 this.dirty = false;
1324 this.select(0);
1684 this.select(0);
1325 this.scroll_to_top();
1685 this.scroll_to_top();
1326 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1686 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1327 msg = "This notebook has been converted from an older " +
1687 msg = "This notebook has been converted from an older " +
1328 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1688 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1329 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1689 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1330 "newer notebook format will be used and older verions of IPython " +
1690 "newer notebook format will be used and older verions of IPython " +
1331 "may not be able to read it. To keep the older version, close the " +
1691 "may not be able to read it. To keep the older version, close the " +
1332 "notebook without saving it.";
1692 "notebook without saving it.";
1333 var dialog = $('<div/>');
1693 var dialog = $('<div/>');
1334 dialog.html(msg);
1694 dialog.html(msg);
1335 this.element.append(dialog);
1695 this.element.append(dialog);
1336 dialog.dialog({
1696 dialog.dialog({
1337 resizable: false,
1697 resizable: false,
1338 modal: true,
1698 modal: true,
1339 title: "Notebook converted",
1699 title: "Notebook converted",
1340 closeText: "",
1700 closeText: "",
1341 close: function(event, ui) {$(this).dialog('destroy').remove();},
1701 close: function(event, ui) {$(this).dialog('destroy').remove();},
1342 buttons : {
1702 buttons : {
1343 "OK": function () {
1703 "OK": function () {
1344 $(this).dialog('close');
1704 $(this).dialog('close');
1345 }
1705 }
1346 },
1706 },
1347 width: 400
1707 width: 400
1348 });
1708 });
1349 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1709 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1350 var that = this;
1710 var that = this;
1351 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1711 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1352 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1712 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1353 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1713 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1354 this_vs + ". You can still work with this notebook, but some features " +
1714 this_vs + ". You can still work with this notebook, but some features " +
1355 "introduced in later notebook versions may not be available."
1715 "introduced in later notebook versions may not be available."
1356
1716
1357 var dialog = $('<div/>');
1717 var dialog = $('<div/>');
1358 dialog.html(msg);
1718 dialog.html(msg);
1359 this.element.append(dialog);
1719 this.element.append(dialog);
1360 dialog.dialog({
1720 dialog.dialog({
1361 resizable: false,
1721 resizable: false,
1362 modal: true,
1722 modal: true,
1363 title: "Newer Notebook",
1723 title: "Newer Notebook",
1364 closeText: "",
1724 closeText: "",
1365 close: function(event, ui) {$(this).dialog('destroy').remove();},
1725 close: function(event, ui) {$(this).dialog('destroy').remove();},
1366 buttons : {
1726 buttons : {
1367 "OK": function () {
1727 "OK": function () {
1368 $(this).dialog('close');
1728 $(this).dialog('close');
1369 }
1729 }
1370 },
1730 },
1371 width: 400
1731 width: 400
1372 });
1732 });
1373
1733
1374 }
1734 }
1375 // Create the kernel after the notebook is completely loaded to prevent
1735 // Create the kernel after the notebook is completely loaded to prevent
1376 // code execution upon loading, which is a security risk.
1736 // code execution upon loading, which is a security risk.
1377 if (! this.read_only) {
1737 if (! this.read_only) {
1378 this.start_kernel();
1738 this.start_kernel();
1379 }
1739 }
1380 $([IPython.events]).trigger('notebook_loaded.Notebook');
1740 $([IPython.events]).trigger('notebook_loaded.Notebook');
1381 };
1741 };
1382
1742
1383
1743 /**
1744 * Failure callback for loading a notebook from the server.
1745 *
1746 * @method load_notebook_error
1747 * @param {jqXHR} xhr jQuery Ajax object
1748 * @param {String} textStatus Description of response status
1749 * @param {String} errorThrow HTTP error message
1750 */
1384 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1751 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1385 if (xhr.status === 500) {
1752 if (xhr.status === 500) {
1386 var msg = "An error occurred while loading this notebook. Most likely " +
1753 var msg = "An error occurred while loading this notebook. Most likely " +
1387 "this notebook is in a newer format than is supported by this " +
1754 "this notebook is in a newer format than is supported by this " +
1388 "version of IPython. This version can load notebook formats " +
1755 "version of IPython. This version can load notebook formats " +
1389 "v"+this.nbformat+" or earlier.";
1756 "v"+this.nbformat+" or earlier.";
1390 var dialog = $('<div/>');
1757 var dialog = $('<div/>');
1391 dialog.html(msg);
1758 dialog.html(msg);
1392 this.element.append(dialog);
1759 this.element.append(dialog);
1393 dialog.dialog({
1760 dialog.dialog({
1394 resizable: false,
1761 resizable: false,
1395 modal: true,
1762 modal: true,
1396 title: "Error loading notebook",
1763 title: "Error loading notebook",
1397 closeText: "",
1764 closeText: "",
1398 close: function(event, ui) {$(this).dialog('destroy').remove();},
1765 close: function(event, ui) {$(this).dialog('destroy').remove();},
1399 buttons : {
1766 buttons : {
1400 "OK": function () {
1767 "OK": function () {
1401 $(this).dialog('close');
1768 $(this).dialog('close');
1402 }
1769 }
1403 },
1770 },
1404 width: 400
1771 width: 400
1405 });
1772 });
1406 }
1773 }
1407 }
1774 }
1408
1775
1409 IPython.Notebook = Notebook;
1776 IPython.Notebook = Notebook;
1410
1777
1411
1778
1412 return IPython;
1779 return IPython;
1413
1780
1414 }(IPython));
1781 }(IPython));
1415
1782
General Comments 0
You need to be logged in to leave comments. Login now