##// END OF EJS Templates
Start of work to make notebook.html requirejs friendly.
Jonathan Frederic -
Show More
@@ -1,21 +1,9 b''
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
3
8 //============================================================================
4 require(['base/js/namespace', 'base/js/page'], function(IPython, Page) {
9 // On document ready
5 IPython.page = new Page();
10 //============================================================================
11
12
13 $(document).ready(function () {
14
15 IPython.page = new IPython.Page();
16 $('button#login_submit').addClass("btn btn-default");
6 $('button#login_submit').addClass("btn btn-default");
17 IPython.page.show();
7 IPython.page.show();
18 $('input#password_input').focus();
8 $('input#password_input').focus();
19
20 });
9 });
21
@@ -1,20 +1,8 b''
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
3
8 //============================================================================
4 require(['base/js/namespace', 'base/js/page'], function(IPython, Page) {
9 // On document ready
5 IPython.page = new Page();
10 //============================================================================
11
12
13 $(document).ready(function () {
14
15 IPython.page = new IPython.Page();
16 $('#ipython-main-app').addClass('border-box-sizing');
6 $('#ipython-main-app').addClass('border-box-sizing');
17 IPython.page.show();
7 IPython.page.show();
18
19 });
8 });
20
@@ -1,557 +1,576 b''
1 "components/codemirror/lib/codemirror.js",
2 // Set codemirror version.
3 // CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
4 "components/codemirror/addon/mode/loadmode.js",
5 "components/codemirror/addon/mode/multiplex.js",
6 "components/codemirror/addon/mode/overlay.js",
7 "components/codemirror/addon/edit/matchbrackets.js",
8 "components/codemirror/addon/edit/closebrackets.js",
9 "components/codemirror/addon/comment/comment.js",
10 "components/codemirror/mode/htmlmixed/htmlmixed.js",
11 "components/codemirror/mode/xml/xml.js",
12 "components/codemirror/mode/javascript/javascript.js",
13 "components/codemirror/mode/css/css.js",
14 "components/codemirror/mode/rst/rst.js",
15 "components/codemirror/mode/markdown/markdown.js",
16 "components/codemirror/mode/python/python.js",
17 "notebook/js/codemirror-ipython.js",
18 "notebook/js/codemirror-ipythongfm.js",
19
1 //----------------------------------------------------------------------------
20 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
21 // Copyright (C) 2008-2011 The IPython Development Team
3 //
22 //
4 // Distributed under the terms of the BSD License. The full license is in
23 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
24 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
25 //----------------------------------------------------------------------------
7
26
8 //============================================================================
27 //============================================================================
9 // Cell
28 // Cell
10 //============================================================================
29 //============================================================================
11 /**
30 /**
12 * An extendable module that provide base functionnality to create cell for notebook.
31 * An extendable module that provide base functionnality to create cell for notebook.
13 * @module IPython
32 * @module IPython
14 * @namespace IPython
33 * @namespace IPython
15 * @submodule Cell
34 * @submodule Cell
16 */
35 */
17
36
18 var IPython = (function (IPython) {
37 var IPython = (function (IPython) {
19 "use strict";
38 "use strict";
20
39
21 var utils = IPython.utils;
40 var utils = IPython.utils;
22 var keycodes = IPython.keyboard.keycodes;
41 var keycodes = IPython.keyboard.keycodes;
23
42
24 /**
43 /**
25 * The Base `Cell` class from which to inherit
44 * The Base `Cell` class from which to inherit
26 * @class Cell
45 * @class Cell
27 **/
46 **/
28
47
29 /*
48 /*
30 * @constructor
49 * @constructor
31 *
50 *
32 * @param {object|undefined} [options]
51 * @param {object|undefined} [options]
33 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
52 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
34 */
53 */
35 var Cell = function (options) {
54 var Cell = function (options) {
36
55
37 options = this.mergeopt(Cell, options);
56 options = this.mergeopt(Cell, options);
38 // superclass default overwrite our default
57 // superclass default overwrite our default
39
58
40 this.placeholder = options.placeholder || '';
59 this.placeholder = options.placeholder || '';
41 this.read_only = options.cm_config.readOnly;
60 this.read_only = options.cm_config.readOnly;
42 this.selected = false;
61 this.selected = false;
43 this.rendered = false;
62 this.rendered = false;
44 this.mode = 'command';
63 this.mode = 'command';
45 this.metadata = {};
64 this.metadata = {};
46 // load this from metadata later ?
65 // load this from metadata later ?
47 this.user_highlight = 'auto';
66 this.user_highlight = 'auto';
48 this.cm_config = options.cm_config;
67 this.cm_config = options.cm_config;
49 this.cell_id = utils.uuid();
68 this.cell_id = utils.uuid();
50 this._options = options;
69 this._options = options;
51
70
52 // For JS VM engines optimization, attributes should be all set (even
71 // For JS VM engines optimization, attributes should be all set (even
53 // to null) in the constructor, and if possible, if different subclass
72 // to null) in the constructor, and if possible, if different subclass
54 // have new attributes with same name, they should be created in the
73 // have new attributes with same name, they should be created in the
55 // same order. Easiest is to create and set to null in parent class.
74 // same order. Easiest is to create and set to null in parent class.
56
75
57 this.element = null;
76 this.element = null;
58 this.cell_type = this.cell_type || null;
77 this.cell_type = this.cell_type || null;
59 this.code_mirror = null;
78 this.code_mirror = null;
60
79
61 this.create_element();
80 this.create_element();
62 if (this.element !== null) {
81 if (this.element !== null) {
63 this.element.data("cell", this);
82 this.element.data("cell", this);
64 this.bind_events();
83 this.bind_events();
65 this.init_classes();
84 this.init_classes();
66 }
85 }
67 };
86 };
68
87
69 Cell.options_default = {
88 Cell.options_default = {
70 cm_config : {
89 cm_config : {
71 indentUnit : 4,
90 indentUnit : 4,
72 readOnly: false,
91 readOnly: false,
73 theme: "default",
92 theme: "default",
74 extraKeys: {
93 extraKeys: {
75 "Cmd-Right":"goLineRight",
94 "Cmd-Right":"goLineRight",
76 "End":"goLineRight",
95 "End":"goLineRight",
77 "Cmd-Left":"goLineLeft"
96 "Cmd-Left":"goLineLeft"
78 }
97 }
79 }
98 }
80 };
99 };
81
100
82 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
101 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
83 // by disabling drag/drop altogether on Safari
102 // by disabling drag/drop altogether on Safari
84 // https://github.com/marijnh/CodeMirror/issues/332
103 // https://github.com/marijnh/CodeMirror/issues/332
85 if (utils.browser[0] == "Safari") {
104 if (utils.browser[0] == "Safari") {
86 Cell.options_default.cm_config.dragDrop = false;
105 Cell.options_default.cm_config.dragDrop = false;
87 }
106 }
88
107
89 Cell.prototype.mergeopt = function(_class, options, overwrite){
108 Cell.prototype.mergeopt = function(_class, options, overwrite){
90 options = options || {};
109 options = options || {};
91 overwrite = overwrite || {};
110 overwrite = overwrite || {};
92 return $.extend(true, {}, _class.options_default, options, overwrite);
111 return $.extend(true, {}, _class.options_default, options, overwrite);
93 };
112 };
94
113
95 /**
114 /**
96 * Empty. Subclasses must implement create_element.
115 * Empty. Subclasses must implement create_element.
97 * This should contain all the code to create the DOM element in notebook
116 * This should contain all the code to create the DOM element in notebook
98 * and will be called by Base Class constructor.
117 * and will be called by Base Class constructor.
99 * @method create_element
118 * @method create_element
100 */
119 */
101 Cell.prototype.create_element = function () {
120 Cell.prototype.create_element = function () {
102 };
121 };
103
122
104 Cell.prototype.init_classes = function () {
123 Cell.prototype.init_classes = function () {
105 // Call after this.element exists to initialize the css classes
124 // Call after this.element exists to initialize the css classes
106 // related to selected, rendered and mode.
125 // related to selected, rendered and mode.
107 if (this.selected) {
126 if (this.selected) {
108 this.element.addClass('selected');
127 this.element.addClass('selected');
109 } else {
128 } else {
110 this.element.addClass('unselected');
129 this.element.addClass('unselected');
111 }
130 }
112 if (this.rendered) {
131 if (this.rendered) {
113 this.element.addClass('rendered');
132 this.element.addClass('rendered');
114 } else {
133 } else {
115 this.element.addClass('unrendered');
134 this.element.addClass('unrendered');
116 }
135 }
117 if (this.mode === 'edit') {
136 if (this.mode === 'edit') {
118 this.element.addClass('edit_mode');
137 this.element.addClass('edit_mode');
119 } else {
138 } else {
120 this.element.addClass('command_mode');
139 this.element.addClass('command_mode');
121 }
140 }
122 };
141 };
123
142
124 /**
143 /**
125 * Subclasses can implement override bind_events.
144 * Subclasses can implement override bind_events.
126 * Be carefull to call the parent method when overwriting as it fires event.
145 * Be carefull to call the parent method when overwriting as it fires event.
127 * this will be triggerd after create_element in constructor.
146 * this will be triggerd after create_element in constructor.
128 * @method bind_events
147 * @method bind_events
129 */
148 */
130 Cell.prototype.bind_events = function () {
149 Cell.prototype.bind_events = function () {
131 var that = this;
150 var that = this;
132 // We trigger events so that Cell doesn't have to depend on Notebook.
151 // We trigger events so that Cell doesn't have to depend on Notebook.
133 that.element.click(function (event) {
152 that.element.click(function (event) {
134 if (!that.selected) {
153 if (!that.selected) {
135 $([IPython.events]).trigger('select.Cell', {'cell':that});
154 $([IPython.events]).trigger('select.Cell', {'cell':that});
136 }
155 }
137 });
156 });
138 that.element.focusin(function (event) {
157 that.element.focusin(function (event) {
139 if (!that.selected) {
158 if (!that.selected) {
140 $([IPython.events]).trigger('select.Cell', {'cell':that});
159 $([IPython.events]).trigger('select.Cell', {'cell':that});
141 }
160 }
142 });
161 });
143 if (this.code_mirror) {
162 if (this.code_mirror) {
144 this.code_mirror.on("change", function(cm, change) {
163 this.code_mirror.on("change", function(cm, change) {
145 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
164 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
146 });
165 });
147 }
166 }
148 if (this.code_mirror) {
167 if (this.code_mirror) {
149 this.code_mirror.on('focus', function(cm, change) {
168 this.code_mirror.on('focus', function(cm, change) {
150 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
169 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
151 });
170 });
152 }
171 }
153 if (this.code_mirror) {
172 if (this.code_mirror) {
154 this.code_mirror.on('blur', function(cm, change) {
173 this.code_mirror.on('blur', function(cm, change) {
155 $([IPython.events]).trigger('command_mode.Cell', {cell: that});
174 $([IPython.events]).trigger('command_mode.Cell', {cell: that});
156 });
175 });
157 }
176 }
158 };
177 };
159
178
160 /**
179 /**
161 * This method gets called in CodeMirror's onKeyDown/onKeyPress
180 * This method gets called in CodeMirror's onKeyDown/onKeyPress
162 * handlers and is used to provide custom key handling.
181 * handlers and is used to provide custom key handling.
163 *
182 *
164 * To have custom handling, subclasses should override this method, but still call it
183 * To have custom handling, subclasses should override this method, but still call it
165 * in order to process the Edit mode keyboard shortcuts.
184 * in order to process the Edit mode keyboard shortcuts.
166 *
185 *
167 * @method handle_codemirror_keyevent
186 * @method handle_codemirror_keyevent
168 * @param {CodeMirror} editor - The codemirror instance bound to the cell
187 * @param {CodeMirror} editor - The codemirror instance bound to the cell
169 * @param {event} event - key press event which either should or should not be handled by CodeMirror
188 * @param {event} event - key press event which either should or should not be handled by CodeMirror
170 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
189 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
171 */
190 */
172 Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
191 Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
173 var that = this;
192 var that = this;
174 var shortcuts = IPython.keyboard_manager.edit_shortcuts;
193 var shortcuts = IPython.keyboard_manager.edit_shortcuts;
175
194
176 // if this is an edit_shortcuts shortcut, the global keyboard/shortcut
195 // if this is an edit_shortcuts shortcut, the global keyboard/shortcut
177 // manager will handle it
196 // manager will handle it
178 if (shortcuts.handles(event)) { return true; }
197 if (shortcuts.handles(event)) { return true; }
179
198
180 return false;
199 return false;
181 };
200 };
182
201
183
202
184 /**
203 /**
185 * Triger typsetting of math by mathjax on current cell element
204 * Triger typsetting of math by mathjax on current cell element
186 * @method typeset
205 * @method typeset
187 */
206 */
188 Cell.prototype.typeset = function () {
207 Cell.prototype.typeset = function () {
189 if (window.MathJax) {
208 if (window.MathJax) {
190 var cell_math = this.element.get(0);
209 var cell_math = this.element.get(0);
191 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
210 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
192 }
211 }
193 };
212 };
194
213
195 /**
214 /**
196 * handle cell level logic when a cell is selected
215 * handle cell level logic when a cell is selected
197 * @method select
216 * @method select
198 * @return is the action being taken
217 * @return is the action being taken
199 */
218 */
200 Cell.prototype.select = function () {
219 Cell.prototype.select = function () {
201 if (!this.selected) {
220 if (!this.selected) {
202 this.element.addClass('selected');
221 this.element.addClass('selected');
203 this.element.removeClass('unselected');
222 this.element.removeClass('unselected');
204 this.selected = true;
223 this.selected = true;
205 return true;
224 return true;
206 } else {
225 } else {
207 return false;
226 return false;
208 }
227 }
209 };
228 };
210
229
211 /**
230 /**
212 * handle cell level logic when a cell is unselected
231 * handle cell level logic when a cell is unselected
213 * @method unselect
232 * @method unselect
214 * @return is the action being taken
233 * @return is the action being taken
215 */
234 */
216 Cell.prototype.unselect = function () {
235 Cell.prototype.unselect = function () {
217 if (this.selected) {
236 if (this.selected) {
218 this.element.addClass('unselected');
237 this.element.addClass('unselected');
219 this.element.removeClass('selected');
238 this.element.removeClass('selected');
220 this.selected = false;
239 this.selected = false;
221 return true;
240 return true;
222 } else {
241 } else {
223 return false;
242 return false;
224 }
243 }
225 };
244 };
226
245
227 /**
246 /**
228 * handle cell level logic when a cell is rendered
247 * handle cell level logic when a cell is rendered
229 * @method render
248 * @method render
230 * @return is the action being taken
249 * @return is the action being taken
231 */
250 */
232 Cell.prototype.render = function () {
251 Cell.prototype.render = function () {
233 if (!this.rendered) {
252 if (!this.rendered) {
234 this.element.addClass('rendered');
253 this.element.addClass('rendered');
235 this.element.removeClass('unrendered');
254 this.element.removeClass('unrendered');
236 this.rendered = true;
255 this.rendered = true;
237 return true;
256 return true;
238 } else {
257 } else {
239 return false;
258 return false;
240 }
259 }
241 };
260 };
242
261
243 /**
262 /**
244 * handle cell level logic when a cell is unrendered
263 * handle cell level logic when a cell is unrendered
245 * @method unrender
264 * @method unrender
246 * @return is the action being taken
265 * @return is the action being taken
247 */
266 */
248 Cell.prototype.unrender = function () {
267 Cell.prototype.unrender = function () {
249 if (this.rendered) {
268 if (this.rendered) {
250 this.element.addClass('unrendered');
269 this.element.addClass('unrendered');
251 this.element.removeClass('rendered');
270 this.element.removeClass('rendered');
252 this.rendered = false;
271 this.rendered = false;
253 return true;
272 return true;
254 } else {
273 } else {
255 return false;
274 return false;
256 }
275 }
257 };
276 };
258
277
259 /**
278 /**
260 * Delegates keyboard shortcut handling to either IPython keyboard
279 * Delegates keyboard shortcut handling to either IPython keyboard
261 * manager when in command mode, or CodeMirror when in edit mode
280 * manager when in command mode, or CodeMirror when in edit mode
262 *
281 *
263 * @method handle_keyevent
282 * @method handle_keyevent
264 * @param {CodeMirror} editor - The codemirror instance bound to the cell
283 * @param {CodeMirror} editor - The codemirror instance bound to the cell
265 * @param {event} - key event to be handled
284 * @param {event} - key event to be handled
266 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
285 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
267 */
286 */
268 Cell.prototype.handle_keyevent = function (editor, event) {
287 Cell.prototype.handle_keyevent = function (editor, event) {
269
288
270 // console.log('CM', this.mode, event.which, event.type)
289 // console.log('CM', this.mode, event.which, event.type)
271
290
272 if (this.mode === 'command') {
291 if (this.mode === 'command') {
273 return true;
292 return true;
274 } else if (this.mode === 'edit') {
293 } else if (this.mode === 'edit') {
275 return this.handle_codemirror_keyevent(editor, event);
294 return this.handle_codemirror_keyevent(editor, event);
276 }
295 }
277 };
296 };
278
297
279 /**
298 /**
280 * @method at_top
299 * @method at_top
281 * @return {Boolean}
300 * @return {Boolean}
282 */
301 */
283 Cell.prototype.at_top = function () {
302 Cell.prototype.at_top = function () {
284 var cm = this.code_mirror;
303 var cm = this.code_mirror;
285 var cursor = cm.getCursor();
304 var cursor = cm.getCursor();
286 if (cursor.line === 0 && cursor.ch === 0) {
305 if (cursor.line === 0 && cursor.ch === 0) {
287 return true;
306 return true;
288 }
307 }
289 return false;
308 return false;
290 };
309 };
291
310
292 /**
311 /**
293 * @method at_bottom
312 * @method at_bottom
294 * @return {Boolean}
313 * @return {Boolean}
295 * */
314 * */
296 Cell.prototype.at_bottom = function () {
315 Cell.prototype.at_bottom = function () {
297 var cm = this.code_mirror;
316 var cm = this.code_mirror;
298 var cursor = cm.getCursor();
317 var cursor = cm.getCursor();
299 if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
318 if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
300 return true;
319 return true;
301 }
320 }
302 return false;
321 return false;
303 };
322 };
304
323
305 /**
324 /**
306 * enter the command mode for the cell
325 * enter the command mode for the cell
307 * @method command_mode
326 * @method command_mode
308 * @return is the action being taken
327 * @return is the action being taken
309 */
328 */
310 Cell.prototype.command_mode = function () {
329 Cell.prototype.command_mode = function () {
311 if (this.mode !== 'command') {
330 if (this.mode !== 'command') {
312 this.element.addClass('command_mode');
331 this.element.addClass('command_mode');
313 this.element.removeClass('edit_mode');
332 this.element.removeClass('edit_mode');
314 this.mode = 'command';
333 this.mode = 'command';
315 return true;
334 return true;
316 } else {
335 } else {
317 return false;
336 return false;
318 }
337 }
319 };
338 };
320
339
321 /**
340 /**
322 * enter the edit mode for the cell
341 * enter the edit mode for the cell
323 * @method command_mode
342 * @method command_mode
324 * @return is the action being taken
343 * @return is the action being taken
325 */
344 */
326 Cell.prototype.edit_mode = function () {
345 Cell.prototype.edit_mode = function () {
327 if (this.mode !== 'edit') {
346 if (this.mode !== 'edit') {
328 this.element.addClass('edit_mode');
347 this.element.addClass('edit_mode');
329 this.element.removeClass('command_mode');
348 this.element.removeClass('command_mode');
330 this.mode = 'edit';
349 this.mode = 'edit';
331 return true;
350 return true;
332 } else {
351 } else {
333 return false;
352 return false;
334 }
353 }
335 };
354 };
336
355
337 /**
356 /**
338 * Focus the cell in the DOM sense
357 * Focus the cell in the DOM sense
339 * @method focus_cell
358 * @method focus_cell
340 */
359 */
341 Cell.prototype.focus_cell = function () {
360 Cell.prototype.focus_cell = function () {
342 this.element.focus();
361 this.element.focus();
343 };
362 };
344
363
345 /**
364 /**
346 * Focus the editor area so a user can type
365 * Focus the editor area so a user can type
347 *
366 *
348 * NOTE: If codemirror is focused via a mouse click event, you don't want to
367 * NOTE: If codemirror is focused via a mouse click event, you don't want to
349 * call this because it will cause a page jump.
368 * call this because it will cause a page jump.
350 * @method focus_editor
369 * @method focus_editor
351 */
370 */
352 Cell.prototype.focus_editor = function () {
371 Cell.prototype.focus_editor = function () {
353 this.refresh();
372 this.refresh();
354 this.code_mirror.focus();
373 this.code_mirror.focus();
355 };
374 };
356
375
357 /**
376 /**
358 * Refresh codemirror instance
377 * Refresh codemirror instance
359 * @method refresh
378 * @method refresh
360 */
379 */
361 Cell.prototype.refresh = function () {
380 Cell.prototype.refresh = function () {
362 this.code_mirror.refresh();
381 this.code_mirror.refresh();
363 };
382 };
364
383
365 /**
384 /**
366 * should be overritten by subclass
385 * should be overritten by subclass
367 * @method get_text
386 * @method get_text
368 */
387 */
369 Cell.prototype.get_text = function () {
388 Cell.prototype.get_text = function () {
370 };
389 };
371
390
372 /**
391 /**
373 * should be overritten by subclass
392 * should be overritten by subclass
374 * @method set_text
393 * @method set_text
375 * @param {string} text
394 * @param {string} text
376 */
395 */
377 Cell.prototype.set_text = function (text) {
396 Cell.prototype.set_text = function (text) {
378 };
397 };
379
398
380 /**
399 /**
381 * should be overritten by subclass
400 * should be overritten by subclass
382 * serialise cell to json.
401 * serialise cell to json.
383 * @method toJSON
402 * @method toJSON
384 **/
403 **/
385 Cell.prototype.toJSON = function () {
404 Cell.prototype.toJSON = function () {
386 var data = {};
405 var data = {};
387 data.metadata = this.metadata;
406 data.metadata = this.metadata;
388 data.cell_type = this.cell_type;
407 data.cell_type = this.cell_type;
389 return data;
408 return data;
390 };
409 };
391
410
392
411
393 /**
412 /**
394 * should be overritten by subclass
413 * should be overritten by subclass
395 * @method fromJSON
414 * @method fromJSON
396 **/
415 **/
397 Cell.prototype.fromJSON = function (data) {
416 Cell.prototype.fromJSON = function (data) {
398 if (data.metadata !== undefined) {
417 if (data.metadata !== undefined) {
399 this.metadata = data.metadata;
418 this.metadata = data.metadata;
400 }
419 }
401 this.celltoolbar.rebuild();
420 this.celltoolbar.rebuild();
402 };
421 };
403
422
404
423
405 /**
424 /**
406 * can the cell be split into two cells
425 * can the cell be split into two cells
407 * @method is_splittable
426 * @method is_splittable
408 **/
427 **/
409 Cell.prototype.is_splittable = function () {
428 Cell.prototype.is_splittable = function () {
410 return true;
429 return true;
411 };
430 };
412
431
413
432
414 /**
433 /**
415 * can the cell be merged with other cells
434 * can the cell be merged with other cells
416 * @method is_mergeable
435 * @method is_mergeable
417 **/
436 **/
418 Cell.prototype.is_mergeable = function () {
437 Cell.prototype.is_mergeable = function () {
419 return true;
438 return true;
420 };
439 };
421
440
422
441
423 /**
442 /**
424 * @return {String} - the text before the cursor
443 * @return {String} - the text before the cursor
425 * @method get_pre_cursor
444 * @method get_pre_cursor
426 **/
445 **/
427 Cell.prototype.get_pre_cursor = function () {
446 Cell.prototype.get_pre_cursor = function () {
428 var cursor = this.code_mirror.getCursor();
447 var cursor = this.code_mirror.getCursor();
429 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
448 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
430 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
449 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
431 return text;
450 return text;
432 };
451 };
433
452
434
453
435 /**
454 /**
436 * @return {String} - the text after the cursor
455 * @return {String} - the text after the cursor
437 * @method get_post_cursor
456 * @method get_post_cursor
438 **/
457 **/
439 Cell.prototype.get_post_cursor = function () {
458 Cell.prototype.get_post_cursor = function () {
440 var cursor = this.code_mirror.getCursor();
459 var cursor = this.code_mirror.getCursor();
441 var last_line_num = this.code_mirror.lineCount()-1;
460 var last_line_num = this.code_mirror.lineCount()-1;
442 var last_line_len = this.code_mirror.getLine(last_line_num).length;
461 var last_line_len = this.code_mirror.getLine(last_line_num).length;
443 var end = {line:last_line_num, ch:last_line_len};
462 var end = {line:last_line_num, ch:last_line_len};
444 var text = this.code_mirror.getRange(cursor, end);
463 var text = this.code_mirror.getRange(cursor, end);
445 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
464 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
446 return text;
465 return text;
447 };
466 };
448
467
449 /**
468 /**
450 * Show/Hide CodeMirror LineNumber
469 * Show/Hide CodeMirror LineNumber
451 * @method show_line_numbers
470 * @method show_line_numbers
452 *
471 *
453 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
472 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
454 **/
473 **/
455 Cell.prototype.show_line_numbers = function (value) {
474 Cell.prototype.show_line_numbers = function (value) {
456 this.code_mirror.setOption('lineNumbers', value);
475 this.code_mirror.setOption('lineNumbers', value);
457 this.code_mirror.refresh();
476 this.code_mirror.refresh();
458 };
477 };
459
478
460 /**
479 /**
461 * Toggle CodeMirror LineNumber
480 * Toggle CodeMirror LineNumber
462 * @method toggle_line_numbers
481 * @method toggle_line_numbers
463 **/
482 **/
464 Cell.prototype.toggle_line_numbers = function () {
483 Cell.prototype.toggle_line_numbers = function () {
465 var val = this.code_mirror.getOption('lineNumbers');
484 var val = this.code_mirror.getOption('lineNumbers');
466 this.show_line_numbers(!val);
485 this.show_line_numbers(!val);
467 };
486 };
468
487
469 /**
488 /**
470 * Force codemirror highlight mode
489 * Force codemirror highlight mode
471 * @method force_highlight
490 * @method force_highlight
472 * @param {object} - CodeMirror mode
491 * @param {object} - CodeMirror mode
473 **/
492 **/
474 Cell.prototype.force_highlight = function(mode) {
493 Cell.prototype.force_highlight = function(mode) {
475 this.user_highlight = mode;
494 this.user_highlight = mode;
476 this.auto_highlight();
495 this.auto_highlight();
477 };
496 };
478
497
479 /**
498 /**
480 * Try to autodetect cell highlight mode, or use selected mode
499 * Try to autodetect cell highlight mode, or use selected mode
481 * @methods _auto_highlight
500 * @methods _auto_highlight
482 * @private
501 * @private
483 * @param {String|object|undefined} - CodeMirror mode | 'auto'
502 * @param {String|object|undefined} - CodeMirror mode | 'auto'
484 **/
503 **/
485 Cell.prototype._auto_highlight = function (modes) {
504 Cell.prototype._auto_highlight = function (modes) {
486 //Here we handle manually selected modes
505 //Here we handle manually selected modes
487 var mode;
506 var mode;
488 if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
507 if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
489 {
508 {
490 mode = this.user_highlight;
509 mode = this.user_highlight;
491 CodeMirror.autoLoadMode(this.code_mirror, mode);
510 CodeMirror.autoLoadMode(this.code_mirror, mode);
492 this.code_mirror.setOption('mode', mode);
511 this.code_mirror.setOption('mode', mode);
493 return;
512 return;
494 }
513 }
495 var current_mode = this.code_mirror.getOption('mode', mode);
514 var current_mode = this.code_mirror.getOption('mode', mode);
496 var first_line = this.code_mirror.getLine(0);
515 var first_line = this.code_mirror.getLine(0);
497 // loop on every pairs
516 // loop on every pairs
498 for(mode in modes) {
517 for(mode in modes) {
499 var regs = modes[mode].reg;
518 var regs = modes[mode].reg;
500 // only one key every time but regexp can't be keys...
519 // only one key every time but regexp can't be keys...
501 for(var i=0; i<regs.length; i++) {
520 for(var i=0; i<regs.length; i++) {
502 // here we handle non magic_modes
521 // here we handle non magic_modes
503 if(first_line.match(regs[i]) !== null) {
522 if(first_line.match(regs[i]) !== null) {
504 if(current_mode == mode){
523 if(current_mode == mode){
505 return;
524 return;
506 }
525 }
507 if (mode.search('magic_') !== 0) {
526 if (mode.search('magic_') !== 0) {
508 this.code_mirror.setOption('mode', mode);
527 this.code_mirror.setOption('mode', mode);
509 CodeMirror.autoLoadMode(this.code_mirror, mode);
528 CodeMirror.autoLoadMode(this.code_mirror, mode);
510 return;
529 return;
511 }
530 }
512 var open = modes[mode].open || "%%";
531 var open = modes[mode].open || "%%";
513 var close = modes[mode].close || "%%end";
532 var close = modes[mode].close || "%%end";
514 var mmode = mode;
533 var mmode = mode;
515 mode = mmode.substr(6);
534 mode = mmode.substr(6);
516 if(current_mode == mode){
535 if(current_mode == mode){
517 return;
536 return;
518 }
537 }
519 CodeMirror.autoLoadMode(this.code_mirror, mode);
538 CodeMirror.autoLoadMode(this.code_mirror, mode);
520 // create on the fly a mode that swhitch between
539 // create on the fly a mode that swhitch between
521 // plain/text and smth else otherwise `%%` is
540 // plain/text and smth else otherwise `%%` is
522 // source of some highlight issues.
541 // source of some highlight issues.
523 // we use patchedGetMode to circumvent a bug in CM
542 // we use patchedGetMode to circumvent a bug in CM
524 CodeMirror.defineMode(mmode , function(config) {
543 CodeMirror.defineMode(mmode , function(config) {
525 return CodeMirror.multiplexingMode(
544 return CodeMirror.multiplexingMode(
526 CodeMirror.patchedGetMode(config, 'text/plain'),
545 CodeMirror.patchedGetMode(config, 'text/plain'),
527 // always set someting on close
546 // always set someting on close
528 {open: open, close: close,
547 {open: open, close: close,
529 mode: CodeMirror.patchedGetMode(config, mode),
548 mode: CodeMirror.patchedGetMode(config, mode),
530 delimStyle: "delimit"
549 delimStyle: "delimit"
531 }
550 }
532 );
551 );
533 });
552 });
534 this.code_mirror.setOption('mode', mmode);
553 this.code_mirror.setOption('mode', mmode);
535 return;
554 return;
536 }
555 }
537 }
556 }
538 }
557 }
539 // fallback on default
558 // fallback on default
540 var default_mode;
559 var default_mode;
541 try {
560 try {
542 default_mode = this._options.cm_config.mode;
561 default_mode = this._options.cm_config.mode;
543 } catch(e) {
562 } catch(e) {
544 default_mode = 'text/plain';
563 default_mode = 'text/plain';
545 }
564 }
546 if( current_mode === default_mode){
565 if( current_mode === default_mode){
547 return;
566 return;
548 }
567 }
549 this.code_mirror.setOption('mode', default_mode);
568 this.code_mirror.setOption('mode', default_mode);
550 };
569 };
551
570
552 IPython.Cell = Cell;
571 IPython.Cell = Cell;
553
572
554 return IPython;
573 return IPython;
555
574
556 }(IPython));
575 }(IPython));
557
576
@@ -1,61 +1,57 b''
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
3
4 // Distributed under the terms of the BSD License. The full license is in
4 define([
5 // the file COPYING, distributed as part of this software.
5 'base/js/namespace',
6 //----------------------------------------------------------------------------
6 'components/jquery/jquery.min',
7
7 ], function(IPython, $) {
8 //============================================================================
9 // Layout
10 //============================================================================
11
12 var IPython = (function (IPython) {
13 "use strict";
8 "use strict";
14
9
15 var LayoutManager = function () {
10 var LayoutManager = function (pager) {
16 this.bind_events();
11 this.bind_events();
12 this.pager = pager;
17 };
13 };
18
14
19 LayoutManager.prototype.bind_events = function () {
15 LayoutManager.prototype.bind_events = function () {
20 $(window).resize($.proxy(this.do_resize,this));
16 $(window).resize($.proxy(this.do_resize,this));
21 };
17 };
22
18
23 LayoutManager.prototype.app_height = function() {
19 LayoutManager.prototype.app_height = function() {
24 var win = $(window);
20 var win = $(window);
25 var w = win.width();
21 var w = win.width();
26 var h = win.height();
22 var h = win.height();
27 var header_height;
23 var header_height;
28 if ($('div#header').css('display') === 'none') {
24 if ($('div#header').css('display') === 'none') {
29 header_height = 0;
25 header_height = 0;
30 } else {
26 } else {
31 header_height = $('div#header').outerHeight(true);
27 header_height = $('div#header').outerHeight(true);
32 }
28 }
33 var menubar_height;
29 var menubar_height;
34 if ($('div#menubar-container').css('display') === 'none') {
30 if ($('div#menubar-container').css('display') === 'none') {
35 menubar_height = 0;
31 menubar_height = 0;
36 } else {
32 } else {
37 menubar_height = $('div#menubar-container').outerHeight(true);
33 menubar_height = $('div#menubar-container').outerHeight(true);
38 }
34 }
39 return h-header_height-menubar_height; // content height
35 return h-header_height-menubar_height; // content height
40 };
36 };
41
37
42 LayoutManager.prototype.do_resize = function () {
38 LayoutManager.prototype.do_resize = function () {
43 var app_height = this.app_height(); // content height
39 var app_height = this.app_height(); // content height
44
40
45 $('#ipython-main-app').height(app_height); // content+padding+border height
41 $('#ipython-main-app').height(app_height); // content+padding+border height
46
42
47 var pager_height = IPython.pager.percentage_height*app_height;
43 var pager_height = this.pager.percentage_height*app_height;
48 var pager_splitter_height = $('div#pager_splitter').outerHeight(true);
44 var pager_splitter_height = $('div#pager_splitter').outerHeight(true);
49 $('div#pager').outerHeight(pager_height);
45 $('div#pager').outerHeight(pager_height);
50 if (IPython.pager.expanded) {
46 if (this.pager.expanded) {
51 $('div#notebook').outerHeight(app_height-pager_height-pager_splitter_height);
47 $('div#notebook').outerHeight(app_height-pager_height-pager_splitter_height);
52 } else {
48 } else {
53 $('div#notebook').outerHeight(app_height-pager_splitter_height);
49 $('div#notebook').outerHeight(app_height-pager_splitter_height);
54 }
50 }
55 };
51 };
56
52
53 // Backwards compatability.
57 IPython.LayoutManager = LayoutManager;
54 IPython.LayoutManager = LayoutManager;
58
55
59 return IPython;
56 return LayoutManager;
60
57 });
61 }(IPython));
@@ -1,127 +1,97 b''
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
3
8 //============================================================================
4 require([
9 // On document ready
5 'base/js/namespace',
10 //============================================================================
6 'notebook/js/notebook',
11
7 'base/js/utils',
12 // for the time beeing, we have to pass marked as a parameter here,
8 'base/js/page',
13 // as injecting require.js make marked not to put itself in the globals,
9 'notebook/js/layoutmanager',
14 // which make both this file fail at setting marked configuration, and textcell.js
10 'base/js/events',
15 // which search marked into global.
11 'auth/js/loginwidget',
16 require(['components/marked/lib/marked',
12 'notebook/js/maintoolbar',
17 'widgets/js/init',
13 'notebook/js/pager',
18 'components/bootstrap-tour/build/js/bootstrap-tour.min'],
14 'notebook/js/quickhelp',
19
15 'notebook/js/menubar',
20 function (marked) {
16 'notebook/js/notificationarea',
17 ], function(
18 IPython,
19 Notebook,
20 Utils,
21 Page,
22 LayoutManager,
23 Events,
24 LoginWidget,
25 MainToolBar,
26 Pager,
27 QuickHelp,
28 MenuBar,
29 NotificationArea
30 ) {
21 "use strict";
31 "use strict";
22
32
23 window.marked = marked;
24
25 // monkey patch CM to be able to syntax highlight cell magics
26 // bug reported upstream,
27 // see https://github.com/marijnh/CodeMirror2/issues/670
28 if(CodeMirror.getMode(1,'text/plain').indent === undefined ){
29 console.log('patching CM for undefined indent');
30 CodeMirror.modes.null = function() {
31 return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0;}};
32 };
33 }
34
35 CodeMirror.patchedGetMode = function(config, mode){
36 var cmmode = CodeMirror.getMode(config, mode);
37 if(cmmode.indent === null) {
38 console.log('patch mode "' , mode, '" on the fly');
39 cmmode.indent = function(){return 0;};
40 }
41 return cmmode;
42 };
43 // end monkey patching CodeMirror
44
45 IPython.mathjaxutils.init();
46
47 $('#ipython-main-app').addClass('border-box-sizing');
33 $('#ipython-main-app').addClass('border-box-sizing');
48 $('div#notebook_panel').addClass('border-box-sizing');
34 $('div#notebook_panel').addClass('border-box-sizing');
49
35
50 var opts = {
36 var opts = {
51 base_url : IPython.utils.get_body_data("baseUrl"),
37 base_url : Utils.get_body_data("baseUrl"),
52 notebook_path : IPython.utils.get_body_data("notebookPath"),
38 notebook_path : Utils.get_body_data("notebookPath"),
53 notebook_name : IPython.utils.get_body_data('notebookName')
39 notebook_name : Utils.get_body_data('notebookName')
54 };
40 };
55
41
56 IPython.page = new IPython.Page();
42 page = new Page();
57 IPython.layout_manager = new IPython.LayoutManager();
43 pager = new Pager('div#pager', 'div#pager_splitter');
58 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
44 layout_manager = new LayoutManager(pager);
59 IPython.quick_help = new IPython.QuickHelp();
45 notebook = new Notebook('div#notebook', opts);
60 try {
46 login_widget = new LoginWidget('span#login_widget', opts);
61 IPython.tour = new IPython.NotebookTour();
47 toolbar = new MainToolBar('#maintoolbar-container');
62 } catch (e) {
48 quick_help = new QuickHelp();
63 console.log("Failed to instantiate Notebook Tour", e);
49 menubar = new MenuBar('#menubar', opts);
64 }
65 IPython.login_widget = new IPython.LoginWidget('span#login_widget', opts);
66 IPython.notebook = new IPython.Notebook('div#notebook', opts);
67 IPython.keyboard_manager = new IPython.KeyboardManager();
68 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
69 IPython.menubar = new IPython.MenuBar('#menubar', opts);
70 IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container');
71 IPython.tooltip = new IPython.Tooltip();
72 IPython.notification_area = new IPython.NotificationArea('#notification_area');
73 IPython.notification_area.init_notification_widgets();
74
50
75 IPython.layout_manager.do_resize();
51 notification_area = new NotificationArea('#notification_area');
52 notification_area.init_notification_widgets();
53
54 layout_manager.do_resize();
76
55
77 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
56 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
78 '<span id="test2" style="font-weight: bold;">x</span>'+
57 '<span id="test2" style="font-weight: bold;">x</span>'+
79 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
58 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
80 var nh = $('#test1').innerHeight();
59 var nh = $('#test1').innerHeight();
81 var bh = $('#test2').innerHeight();
60 var bh = $('#test2').innerHeight();
82 var ih = $('#test3').innerHeight();
61 var ih = $('#test3').innerHeight();
83 if(nh != bh || nh != ih) {
62 if(nh != bh || nh != ih) {
84 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
63 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
85 }
64 }
86 $('#fonttest').remove();
65 $('#fonttest').remove();
87
66
88 IPython.page.show();
67 page.show();
89
68
90 IPython.layout_manager.do_resize();
69 layout_manager.do_resize();
91 var first_load = function () {
70 var first_load = function () {
92 IPython.layout_manager.do_resize();
71 layout_manager.do_resize();
93 var hash = document.location.hash;
72 var hash = document.location.hash;
94 if (hash) {
73 if (hash) {
95 document.location.hash = '';
74 document.location.hash = '';
96 document.location.hash = hash;
75 document.location.hash = hash;
97 }
76 }
98 IPython.notebook.set_autosave_interval(IPython.notebook.minimum_autosave_interval);
77 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
99 // only do this once
78 // only do this once
100 $([IPython.events]).off('notebook_loaded.Notebook', first_load);
79 $([Events]).off('notebook_loaded.Notebook', first_load);
101 };
80 };
102
81
103 $([IPython.events]).on('notebook_loaded.Notebook', first_load);
82 $([Events]).on('notebook_loaded.Notebook', first_load);
104 $([IPython.events]).trigger('app_initialized.NotebookApp');
83 $([Events]).trigger('app_initialized.NotebookApp');
105 IPython.notebook.load_notebook(opts.notebook_name, opts.notebook_path);
84 notebook.load_notebook(opts.notebook_name, opts.notebook_path);
106
85
107 if (marked) {
86 // Backwards compatability.
108 marked.setOptions({
87 IPython.page = page;
109 gfm : true,
88 IPython.layout_manager = layout_manager;
110 tables: true,
89 IPython.notebook = notebook;
111 langPrefix: "language-",
90 IPython.pager = pager;
112 highlight: function(code, lang) {
91 IPython.quick_help = quick_help;
113 if (!lang) {
92 IPython.login_widget = login_widget;
114 // no language, no highlight
93 IPython.menubar = menubar;
115 return code;
94 IPython.toolbar = toolbar;
116 }
95 IPython.notification_area = notification_area;
117 var highlighted;
96 IPython.notification_area = notification_area;
118 try {
119 highlighted = hljs.highlight(lang, code, false);
120 } catch(err) {
121 highlighted = hljs.highlightAuto(code);
122 }
123 return highlighted.value;
124 }
125 });
126 }
127 });
97 });
@@ -1,221 +1,218 b''
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
3
4 // Distributed under the terms of the BSD License. The full license is in
4 define([
5 // the file COPYING, distributed as part of this software.
5 'base/js/namespace',
6 //----------------------------------------------------------------------------
6 'components/jquery/jquery.min',
7
7 'notebook/js/toolbar',
8 //============================================================================
8 ], function(IPython, $, Toolbar) {
9 // ToolBar
10 //============================================================================
11
12 var IPython = (function (IPython) {
13 "use strict";
9 "use strict";
14
10
15 var MainToolBar = function (selector) {
11 var MainToolBar = function (selector, notebook) {
16 IPython.ToolBar.apply(this, arguments);
12 ToolBar.apply(this, arguments);
13 this.notebook = notebook;
17 this.construct();
14 this.construct();
18 this.add_celltype_list();
15 this.add_celltype_list();
19 this.add_celltoolbar_list();
16 this.add_celltoolbar_list();
20 this.bind_events();
17 this.bind_events();
21 };
18 };
22
19
23 MainToolBar.prototype = new IPython.ToolBar();
20 MainToolBar.prototype = new ToolBar();
24
21
25 MainToolBar.prototype.construct = function () {
22 MainToolBar.prototype.construct = function () {
26 this.add_buttons_group([
23 this.add_buttons_group([
27 {
24 {
28 id : 'save_b',
25 id : 'save_b',
29 label : 'Save and Checkpoint',
26 label : 'Save and Checkpoint',
30 icon : 'icon-save',
27 icon : 'icon-save',
31 callback : function () {
28 callback : function () {
32 IPython.notebook.save_checkpoint();
29 this.notebook.save_checkpoint();
33 }
30 }
34 }
31 }
35 ]);
32 ]);
36
33
37 this.add_buttons_group([
34 this.add_buttons_group([
38 {
35 {
39 id : 'insert_below_b',
36 id : 'insert_below_b',
40 label : 'Insert Cell Below',
37 label : 'Insert Cell Below',
41 icon : 'icon-plus-sign',
38 icon : 'icon-plus-sign',
42 callback : function () {
39 callback : function () {
43 IPython.notebook.insert_cell_below('code');
40 this.notebook.insert_cell_below('code');
44 IPython.notebook.select_next();
41 this.notebook.select_next();
45 IPython.notebook.focus_cell();
42 this.notebook.focus_cell();
46 }
43 }
47 }
44 }
48 ],'insert_above_below');
45 ],'insert_above_below');
49
46
50 this.add_buttons_group([
47 this.add_buttons_group([
51 {
48 {
52 id : 'cut_b',
49 id : 'cut_b',
53 label : 'Cut Cell',
50 label : 'Cut Cell',
54 icon : 'icon-cut',
51 icon : 'icon-cut',
55 callback : function () {
52 callback : function () {
56 IPython.notebook.cut_cell();
53 this.notebook.cut_cell();
57 }
54 }
58 },
55 },
59 {
56 {
60 id : 'copy_b',
57 id : 'copy_b',
61 label : 'Copy Cell',
58 label : 'Copy Cell',
62 icon : 'icon-copy',
59 icon : 'icon-copy',
63 callback : function () {
60 callback : function () {
64 IPython.notebook.copy_cell();
61 this.notebook.copy_cell();
65 }
62 }
66 },
63 },
67 {
64 {
68 id : 'paste_b',
65 id : 'paste_b',
69 label : 'Paste Cell Below',
66 label : 'Paste Cell Below',
70 icon : 'icon-paste',
67 icon : 'icon-paste',
71 callback : function () {
68 callback : function () {
72 IPython.notebook.paste_cell_below();
69 this.notebook.paste_cell_below();
73 }
70 }
74 }
71 }
75 ],'cut_copy_paste');
72 ],'cut_copy_paste');
76
73
77 this.add_buttons_group([
74 this.add_buttons_group([
78 {
75 {
79 id : 'move_up_b',
76 id : 'move_up_b',
80 label : 'Move Cell Up',
77 label : 'Move Cell Up',
81 icon : 'icon-arrow-up',
78 icon : 'icon-arrow-up',
82 callback : function () {
79 callback : function () {
83 IPython.notebook.move_cell_up();
80 this.notebook.move_cell_up();
84 }
81 }
85 },
82 },
86 {
83 {
87 id : 'move_down_b',
84 id : 'move_down_b',
88 label : 'Move Cell Down',
85 label : 'Move Cell Down',
89 icon : 'icon-arrow-down',
86 icon : 'icon-arrow-down',
90 callback : function () {
87 callback : function () {
91 IPython.notebook.move_cell_down();
88 this.notebook.move_cell_down();
92 }
89 }
93 }
90 }
94 ],'move_up_down');
91 ],'move_up_down');
95
92
96
93
97 this.add_buttons_group([
94 this.add_buttons_group([
98 {
95 {
99 id : 'run_b',
96 id : 'run_b',
100 label : 'Run Cell',
97 label : 'Run Cell',
101 icon : 'icon-play',
98 icon : 'icon-play',
102 callback : function () {
99 callback : function () {
103 // emulate default shift-enter behavior
100 // emulate default shift-enter behavior
104 IPython.notebook.execute_cell_and_select_below();
101 this.notebook.execute_cell_and_select_below();
105 }
102 }
106 },
103 },
107 {
104 {
108 id : 'interrupt_b',
105 id : 'interrupt_b',
109 label : 'Interrupt',
106 label : 'Interrupt',
110 icon : 'icon-stop',
107 icon : 'icon-stop',
111 callback : function () {
108 callback : function () {
112 IPython.notebook.session.interrupt_kernel();
109 this.notebook.session.interrupt_kernel();
113 }
110 }
114 },
111 },
115 {
112 {
116 id : 'repeat_b',
113 id : 'repeat_b',
117 label : 'Restart Kernel',
114 label : 'Restart Kernel',
118 icon : 'icon-repeat',
115 icon : 'icon-repeat',
119 callback : function () {
116 callback : function () {
120 IPython.notebook.restart_kernel();
117 this.notebook.restart_kernel();
121 }
118 }
122 }
119 }
123 ],'run_int');
120 ],'run_int');
124 };
121 };
125
122
126 MainToolBar.prototype.add_celltype_list = function () {
123 MainToolBar.prototype.add_celltype_list = function () {
127 this.element
124 this.element
128 .append($('<select/>')
125 .append($('<select/>')
129 .attr('id','cell_type')
126 .attr('id','cell_type')
130 .addClass('form-control select-xs')
127 .addClass('form-control select-xs')
131 // .addClass('ui-widget-content')
128 // .addClass('ui-widget-content')
132 .append($('<option/>').attr('value','code').text('Code'))
129 .append($('<option/>').attr('value','code').text('Code'))
133 .append($('<option/>').attr('value','markdown').text('Markdown'))
130 .append($('<option/>').attr('value','markdown').text('Markdown'))
134 .append($('<option/>').attr('value','raw').text('Raw NBConvert'))
131 .append($('<option/>').attr('value','raw').text('Raw NBConvert'))
135 .append($('<option/>').attr('value','heading1').text('Heading 1'))
132 .append($('<option/>').attr('value','heading1').text('Heading 1'))
136 .append($('<option/>').attr('value','heading2').text('Heading 2'))
133 .append($('<option/>').attr('value','heading2').text('Heading 2'))
137 .append($('<option/>').attr('value','heading3').text('Heading 3'))
134 .append($('<option/>').attr('value','heading3').text('Heading 3'))
138 .append($('<option/>').attr('value','heading4').text('Heading 4'))
135 .append($('<option/>').attr('value','heading4').text('Heading 4'))
139 .append($('<option/>').attr('value','heading5').text('Heading 5'))
136 .append($('<option/>').attr('value','heading5').text('Heading 5'))
140 .append($('<option/>').attr('value','heading6').text('Heading 6'))
137 .append($('<option/>').attr('value','heading6').text('Heading 6'))
141 );
138 );
142 };
139 };
143
140
144
141
145 MainToolBar.prototype.add_celltoolbar_list = function () {
142 MainToolBar.prototype.add_celltoolbar_list = function () {
146 var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:');
143 var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:');
147 var select = $('<select/>')
144 var select = $('<select/>')
148 // .addClass('ui-widget-content')
145 // .addClass('ui-widget-content')
149 .attr('id', 'ctb_select')
146 .attr('id', 'ctb_select')
150 .addClass('form-control select-xs')
147 .addClass('form-control select-xs')
151 .append($('<option/>').attr('value', '').text('None'));
148 .append($('<option/>').attr('value', '').text('None'));
152 this.element.append(label).append(select);
149 this.element.append(label).append(select);
153 select.change(function() {
150 select.change(function() {
154 var val = $(this).val()
151 var val = $(this).val();
155 if (val =='') {
152 if (val ==='') {
156 IPython.CellToolbar.global_hide();
153 IPython.CellToolbar.global_hide();
157 delete IPython.notebook.metadata.celltoolbar;
154 delete this.notebook.metadata.celltoolbar;
158 } else {
155 } else {
159 IPython.CellToolbar.global_show();
156 IPython.CellToolbar.global_show();
160 IPython.CellToolbar.activate_preset(val);
157 IPython.CellToolbar.activate_preset(val);
161 IPython.notebook.metadata.celltoolbar = val;
158 this.notebook.metadata.celltoolbar = val;
162 }
159 }
163 });
160 });
164 // Setup the currently registered presets.
161 // Setup the currently registered presets.
165 var presets = IPython.CellToolbar.list_presets();
162 var presets = IPython.CellToolbar.list_presets();
166 for (var i=0; i<presets.length; i++) {
163 for (var i=0; i<presets.length; i++) {
167 var name = presets[i];
164 var name = presets[i];
168 select.append($('<option/>').attr('value', name).text(name));
165 select.append($('<option/>').attr('value', name).text(name));
169 }
166 }
170 // Setup future preset registrations.
167 // Setup future preset registrations.
171 $([IPython.events]).on('preset_added.CellToolbar', function (event, data) {
168 $([IPython.events]).on('preset_added.CellToolbar', function (event, data) {
172 var name = data.name;
169 var name = data.name;
173 select.append($('<option/>').attr('value', name).text(name));
170 select.append($('<option/>').attr('value', name).text(name));
174 });
171 });
175 // Update select value when a preset is activated.
172 // Update select value when a preset is activated.
176 $([IPython.events]).on('preset_activated.CellToolbar', function (event, data) {
173 $([IPython.events]).on('preset_activated.CellToolbar', function (event, data) {
177 if (select.val() !== data.name)
174 if (select.val() !== data.name)
178 select.val(data.name);
175 select.val(data.name);
179 });
176 });
180 };
177 };
181
178
182
179
183 MainToolBar.prototype.bind_events = function () {
180 MainToolBar.prototype.bind_events = function () {
184 var that = this;
181 var that = this;
185
182
186 this.element.find('#cell_type').change(function () {
183 this.element.find('#cell_type').change(function () {
187 var cell_type = $(this).val();
184 var cell_type = $(this).val();
188 if (cell_type === 'code') {
185 if (cell_type === 'code') {
189 IPython.notebook.to_code();
186 this.notebook.to_code();
190 } else if (cell_type === 'markdown') {
187 } else if (cell_type === 'markdown') {
191 IPython.notebook.to_markdown();
188 this.notebook.to_markdown();
192 } else if (cell_type === 'raw') {
189 } else if (cell_type === 'raw') {
193 IPython.notebook.to_raw();
190 this.notebook.to_raw();
194 } else if (cell_type === 'heading1') {
191 } else if (cell_type === 'heading1') {
195 IPython.notebook.to_heading(undefined, 1);
192 this.notebook.to_heading(undefined, 1);
196 } else if (cell_type === 'heading2') {
193 } else if (cell_type === 'heading2') {
197 IPython.notebook.to_heading(undefined, 2);
194 this.notebook.to_heading(undefined, 2);
198 } else if (cell_type === 'heading3') {
195 } else if (cell_type === 'heading3') {
199 IPython.notebook.to_heading(undefined, 3);
196 this.notebook.to_heading(undefined, 3);
200 } else if (cell_type === 'heading4') {
197 } else if (cell_type === 'heading4') {
201 IPython.notebook.to_heading(undefined, 4);
198 this.notebook.to_heading(undefined, 4);
202 } else if (cell_type === 'heading5') {
199 } else if (cell_type === 'heading5') {
203 IPython.notebook.to_heading(undefined, 5);
200 this.notebook.to_heading(undefined, 5);
204 } else if (cell_type === 'heading6') {
201 } else if (cell_type === 'heading6') {
205 IPython.notebook.to_heading(undefined, 6);
202 this.notebook.to_heading(undefined, 6);
206 }
203 }
207 });
204 });
208 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
205 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
209 if (data.cell_type === 'heading') {
206 if (data.cell_type === 'heading') {
210 that.element.find('#cell_type').val(data.cell_type+data.level);
207 that.element.find('#cell_type').val(data.cell_type+data.level);
211 } else {
208 } else {
212 that.element.find('#cell_type').val(data.cell_type);
209 that.element.find('#cell_type').val(data.cell_type);
213 }
210 }
214 });
211 });
215 };
212 };
216
213
214 // Backwards compatability.
217 IPython.MainToolBar = MainToolBar;
215 IPython.MainToolBar = MainToolBar;
218
216
219 return IPython;
217 return MainToolBar;
220
218 });
221 }(IPython));
@@ -1,2430 +1,2545 b''
1 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Copyright (C) 2011 The IPython Development Team
2 // Distributed under the terms of the Modified BSD License.
3 //
3
4 // Distributed under the terms of the BSD License. The full license is in
4
5 // the file COPYING, distributed as part of this software.
5 "components/google-caja/html-css-sanitizer-minified",
6 //----------------------------------------------------------------------------
6
7
7 "components/highlight.js/build/highlight.pack",
8 //============================================================================
8 "dateformat/date.format",
9 // Notebook
9 "base/js/security",
10 //============================================================================
10 "services/kernels/js/kernel",
11
11 "services/kernels/js/comm",
12 var IPython = (function (IPython) {
12 "notebook/js/mathjaxutils",
13 "use strict";
13 "notebook/js/outputarea",
14
14 "notebook/js/cell",
15 var utils = IPython.utils;
15 "notebook/js/codecell",
16 "notebook/js/completer",
17 "notebook/js/notificationwidget",
18 "notebook/js/notificationarea",
19 "notebook/js/tooltip",
20 "notebook/js/config",
21 "notebook/js/main",
22 "notebook/js/contexthint",
23 "notebook/js/celltoolbarpresets/default",
24 "notebook/js/celltoolbarpresets/rawcell",
25 "notebook/js/celltoolbarpresets/slideshow"
26
27 IPython.mathjaxutils.init();
28
29 'components/marked/lib/marked',
30 'widgets/js/init',
31 'components/bootstrap-tour/build/js/bootstrap-tour.min'
32
33 window.marked = marked;
34
35 if (marked) {
36 marked.setOptions({
37 gfm : true,
38 tables: true,
39 langPrefix: "language-",
40 highlight: function(code, lang) {
41 if (!lang) {
42 // no language, no highlight
43 return code;
44 }
45 var highlighted;
46 try {
47 highlighted = hljs.highlight(lang, code, false);
48 } catch(err) {
49 highlighted = hljs.highlightAuto(code);
50 }
51 return highlighted.value;
52 }
53 });
54 }
55
56 // monkey patch CM to be able to syntax highlight cell magics
57 // bug reported upstream,
58 // see https://github.com/marijnh/CodeMirror2/issues/670
59 if(CodeMirror.getMode(1,'text/plain').indent === undefined ){
60 console.log('patching CM for undefined indent');
61 CodeMirror.modes.null = function() {
62 return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0;}};
63 };
64 }
65
66 CodeMirror.patchedGetMode = function(config, mode){
67 var cmmode = CodeMirror.getMode(config, mode);
68 if(cmmode.indent === null) {
69 console.log('patch mode "' , mode, '" on the fly');
70 cmmode.indent = function(){return 0;};
71 }
72 return cmmode;
73 };
74 // end monkey patching CodeMirror
75
76
77 "notebook/js/tour",
78 Tour,
79
80 try {
81 tour = new Tour();
82 } catch (e) {
83 tour = undefined;
84 console.log("Failed to instantiate Notebook Tour", e);
85 }
86
87 IPython.tour = tour;
88
89 "notebook/js/tooltip",
90 Tooltip,
91 tooltip = new Tooltip();
92 IPython.tooltip = tooltip;
93
94
95 define([
96 "base/js/namespace",
97 "components/jquery/jquery.min",
98 "base/js/utils",
99 "notebook/js/keyboardmanager",
100 "notebook/js/savewidget",
101 "base/js/events",
102 "base/js/dialog",
103 "notebook/js/textcell",
104 "notebook/js/codecell",
105 "services/sessions/js/session",
106 "notebook/js/celltoolbar",
107 "base/js/keyboard",
108 "components/jquery-ui/ui/minified/jquery-ui.min",
109 "components/bootstrap/js/bootstrap.min",
110 ], function (
111 IPython,
112 $,
113 Utils,
114 KeyboardManager,
115 SaveWidget,
116 Events,
117 Dialog,
118 Cells,
119 CodeCell,
120 Session,
121 CellToolbar,
122 Keyboard
123 ) {
124
125 keyboard_manager = new KeyboardManager();
126 save_widget = new SaveWidget('span#save_widget');
127 keyboard = new Keyboard();
128
129 // Backwards compatability.
130 IPython.keyboard_manager = keyboard_manager;
131 IPython.save_widget = save_widget;
132 IPython.keyboard = keyboard;
16
133
17 /**
134 /**
18 * A notebook contains and manages cells.
135 * A notebook contains and manages cells.
19 *
136 *
20 * @class Notebook
137 * @class Notebook
21 * @constructor
138 * @constructor
22 * @param {String} selector A jQuery selector for the notebook's DOM element
139 * @param {String} selector A jQuery selector for the notebook's DOM element
23 * @param {Object} [options] A config object
140 * @param {Object} [options] A config object
24 */
141 */
25 var Notebook = function (selector, options) {
142 var Notebook = function (selector, options) {
26 this.options = options = options || {};
143 this.options = options = options || {};
27 this.base_url = options.base_url;
144 this.base_url = options.base_url;
28 this.notebook_path = options.notebook_path;
145 this.notebook_path = options.notebook_path;
29 this.notebook_name = options.notebook_name;
146 this.notebook_name = options.notebook_name;
30 this.element = $(selector);
147 this.element = $(selector);
31 this.element.scroll();
148 this.element.scroll();
32 this.element.data("notebook", this);
149 this.element.data("notebook", this);
33 this.next_prompt_number = 1;
150 this.next_prompt_number = 1;
34 this.session = null;
151 this.session = null;
35 this.kernel = null;
152 this.kernel = null;
36 this.clipboard = null;
153 this.clipboard = null;
37 this.undelete_backup = null;
154 this.undelete_backup = null;
38 this.undelete_index = null;
155 this.undelete_index = null;
39 this.undelete_below = false;
156 this.undelete_below = false;
40 this.paste_enabled = false;
157 this.paste_enabled = false;
41 // It is important to start out in command mode to match the intial mode
158 // It is important to start out in command mode to match the intial mode
42 // of the KeyboardManager.
159 // of the KeyboardManager.
43 this.mode = 'command';
160 this.mode = 'command';
44 this.set_dirty(false);
161 this.set_dirty(false);
45 this.metadata = {};
162 this.metadata = {};
46 this._checkpoint_after_save = false;
163 this._checkpoint_after_save = false;
47 this.last_checkpoint = null;
164 this.last_checkpoint = null;
48 this.checkpoints = [];
165 this.checkpoints = [];
49 this.autosave_interval = 0;
166 this.autosave_interval = 0;
50 this.autosave_timer = null;
167 this.autosave_timer = null;
51 // autosave *at most* every two minutes
168 // autosave *at most* every two minutes
52 this.minimum_autosave_interval = 120000;
169 this.minimum_autosave_interval = 120000;
53 // single worksheet for now
170 // single worksheet for now
54 this.worksheet_metadata = {};
171 this.worksheet_metadata = {};
55 this.notebook_name_blacklist_re = /[\/\\:]/;
172 this.notebook_name_blacklist_re = /[\/\\:]/;
56 this.nbformat = 3; // Increment this when changing the nbformat
173 this.nbformat = 3; // Increment this when changing the nbformat
57 this.nbformat_minor = 0; // Increment this when changing the nbformat
174 this.nbformat_minor = 0; // Increment this when changing the nbformat
58 this.style();
175 this.style();
59 this.create_elements();
176 this.create_elements();
60 this.bind_events();
177 this.bind_events();
61 this.save_notebook = function() { // don't allow save until notebook_loaded
178 this.save_notebook = function() { // don't allow save until notebook_loaded
62 this.save_notebook_error(null, null, "Load failed, save is disabled");
179 this.save_notebook_error(null, null, "Load failed, save is disabled");
63 };
180 };
64 };
181 };
65
182
66 /**
183 /**
67 * Tweak the notebook's CSS style.
184 * Tweak the notebook's CSS style.
68 *
185 *
69 * @method style
186 * @method style
70 */
187 */
71 Notebook.prototype.style = function () {
188 Notebook.prototype.style = function () {
72 $('div#notebook').addClass('border-box-sizing');
189 $('div#notebook').addClass('border-box-sizing');
73 };
190 };
74
191
75 /**
192 /**
76 * Create an HTML and CSS representation of the notebook.
193 * Create an HTML and CSS representation of the notebook.
77 *
194 *
78 * @method create_elements
195 * @method create_elements
79 */
196 */
80 Notebook.prototype.create_elements = function () {
197 Notebook.prototype.create_elements = function () {
81 var that = this;
198 var that = this;
82 this.element.attr('tabindex','-1');
199 this.element.attr('tabindex','-1');
83 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
200 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
84 // We add this end_space div to the end of the notebook div to:
201 // We add this end_space div to the end of the notebook div to:
85 // i) provide a margin between the last cell and the end of the notebook
202 // i) provide a margin between the last cell and the end of the notebook
86 // ii) to prevent the div from scrolling up when the last cell is being
203 // ii) to prevent the div from scrolling up when the last cell is being
87 // edited, but is too low on the page, which browsers will do automatically.
204 // edited, but is too low on the page, which browsers will do automatically.
88 var end_space = $('<div/>').addClass('end_space');
205 var end_space = $('<div/>').addClass('end_space');
89 end_space.dblclick(function (e) {
206 end_space.dblclick(function (e) {
90 var ncells = that.ncells();
207 var ncells = that.ncells();
91 that.insert_cell_below('code',ncells-1);
208 that.insert_cell_below('code',ncells-1);
92 });
209 });
93 this.element.append(this.container);
210 this.element.append(this.container);
94 this.container.append(end_space);
211 this.container.append(end_space);
95 };
212 };
96
213
97 /**
214 /**
98 * Bind JavaScript events: key presses and custom IPython events.
215 * Bind JavaScript events: key presses and custom IPython events.
99 *
216 *
100 * @method bind_events
217 * @method bind_events
101 */
218 */
102 Notebook.prototype.bind_events = function () {
219 Notebook.prototype.bind_events = function () {
103 var that = this;
220 var that = this;
104
221
105 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
222 $([Events]).on('set_next_input.Notebook', function (event, data) {
106 var index = that.find_cell_index(data.cell);
223 var index = that.find_cell_index(data.cell);
107 var new_cell = that.insert_cell_below('code',index);
224 var new_cell = that.insert_cell_below('code',index);
108 new_cell.set_text(data.text);
225 new_cell.set_text(data.text);
109 that.dirty = true;
226 that.dirty = true;
110 });
227 });
111
228
112 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
229 $([Events]).on('set_dirty.Notebook', function (event, data) {
113 that.dirty = data.value;
230 that.dirty = data.value;
114 });
231 });
115
232
116 $([IPython.events]).on('trust_changed.Notebook', function (event, data) {
233 $([Events]).on('trust_changed.Notebook', function (event, data) {
117 that.trusted = data.value;
234 that.trusted = data.value;
118 });
235 });
119
236
120 $([IPython.events]).on('select.Cell', function (event, data) {
237 $([Events]).on('select.Cell', function (event, data) {
121 var index = that.find_cell_index(data.cell);
238 var index = that.find_cell_index(data.cell);
122 that.select(index);
239 that.select(index);
123 });
240 });
124
241
125 $([IPython.events]).on('edit_mode.Cell', function (event, data) {
242 $([Events]).on('edit_mode.Cell', function (event, data) {
126 that.handle_edit_mode(data.cell);
243 that.handle_edit_mode(data.cell);
127 });
244 });
128
245
129 $([IPython.events]).on('command_mode.Cell', function (event, data) {
246 $([Events]).on('command_mode.Cell', function (event, data) {
130 that.handle_command_mode(data.cell);
247 that.handle_command_mode(data.cell);
131 });
248 });
132
249
133 $([IPython.events]).on('status_autorestarting.Kernel', function () {
250 $([Events]).on('status_autorestarting.Kernel', function () {
134 IPython.dialog.modal({
251 Dialog.modal({
135 title: "Kernel Restarting",
252 title: "Kernel Restarting",
136 body: "The kernel appears to have died. It will restart automatically.",
253 body: "The kernel appears to have died. It will restart automatically.",
137 buttons: {
254 buttons: {
138 OK : {
255 OK : {
139 class : "btn-primary"
256 class : "btn-primary"
140 }
257 }
141 }
258 }
142 });
259 });
143 });
260 });
144
261
145 var collapse_time = function (time) {
262 var collapse_time = function (time) {
146 var app_height = $('#ipython-main-app').height(); // content height
263 var app_height = $('#ipython-main-app').height(); // content height
147 var splitter_height = $('div#pager_splitter').outerHeight(true);
264 var splitter_height = $('div#pager_splitter').outerHeight(true);
148 var new_height = app_height - splitter_height;
265 var new_height = app_height - splitter_height;
149 that.element.animate({height : new_height + 'px'}, time);
266 that.element.animate({height : new_height + 'px'}, time);
150 };
267 };
151
268
152 this.element.bind('collapse_pager', function (event, extrap) {
269 this.element.bind('collapse_pager', function (event, extrap) {
153 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
270 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
154 collapse_time(time);
271 collapse_time(time);
155 });
272 });
156
273
157 var expand_time = function (time) {
274 var expand_time = function (time) {
158 var app_height = $('#ipython-main-app').height(); // content height
275 var app_height = $('#ipython-main-app').height(); // content height
159 var splitter_height = $('div#pager_splitter').outerHeight(true);
276 var splitter_height = $('div#pager_splitter').outerHeight(true);
160 var pager_height = $('div#pager').outerHeight(true);
277 var pager_height = $('div#pager').outerHeight(true);
161 var new_height = app_height - pager_height - splitter_height;
278 var new_height = app_height - pager_height - splitter_height;
162 that.element.animate({height : new_height + 'px'}, time);
279 that.element.animate({height : new_height + 'px'}, time);
163 };
280 };
164
281
165 this.element.bind('expand_pager', function (event, extrap) {
282 this.element.bind('expand_pager', function (event, extrap) {
166 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
283 var time = (extrap !== undefined) ? ((extrap.duration !== undefined ) ? extrap.duration : 'fast') : 'fast';
167 expand_time(time);
284 expand_time(time);
168 });
285 });
169
286
170 // Firefox 22 broke $(window).on("beforeunload")
287 // Firefox 22 broke $(window).on("beforeunload")
171 // I'm not sure why or how.
288 // I'm not sure why or how.
172 window.onbeforeunload = function (e) {
289 window.onbeforeunload = function (e) {
173 // TODO: Make killing the kernel configurable.
290 // TODO: Make killing the kernel configurable.
174 var kill_kernel = false;
291 var kill_kernel = false;
175 if (kill_kernel) {
292 if (kill_kernel) {
176 that.session.kill_kernel();
293 that.session.kill_kernel();
177 }
294 }
178 // if we are autosaving, trigger an autosave on nav-away.
295 // if we are autosaving, trigger an autosave on nav-away.
179 // still warn, because if we don't the autosave may fail.
296 // still warn, because if we don't the autosave may fail.
180 if (that.dirty) {
297 if (that.dirty) {
181 if ( that.autosave_interval ) {
298 if ( that.autosave_interval ) {
182 // schedule autosave in a timeout
299 // schedule autosave in a timeout
183 // this gives you a chance to forcefully discard changes
300 // this gives you a chance to forcefully discard changes
184 // by reloading the page if you *really* want to.
301 // by reloading the page if you *really* want to.
185 // the timer doesn't start until you *dismiss* the dialog.
302 // the timer doesn't start until you *dismiss* the dialog.
186 setTimeout(function () {
303 setTimeout(function () {
187 if (that.dirty) {
304 if (that.dirty) {
188 that.save_notebook();
305 that.save_notebook();
189 }
306 }
190 }, 1000);
307 }, 1000);
191 return "Autosave in progress, latest changes may be lost.";
308 return "Autosave in progress, latest changes may be lost.";
192 } else {
309 } else {
193 return "Unsaved changes will be lost.";
310 return "Unsaved changes will be lost.";
194 }
311 }
195 }
312 }
196 // Null is the *only* return value that will make the browser not
313 // Null is the *only* return value that will make the browser not
197 // pop up the "don't leave" dialog.
314 // pop up the "don't leave" dialog.
198 return null;
315 return null;
199 };
316 };
200 };
317 };
201
318
202 /**
319 /**
203 * Set the dirty flag, and trigger the set_dirty.Notebook event
320 * Set the dirty flag, and trigger the set_dirty.Notebook event
204 *
321 *
205 * @method set_dirty
322 * @method set_dirty
206 */
323 */
207 Notebook.prototype.set_dirty = function (value) {
324 Notebook.prototype.set_dirty = function (value) {
208 if (value === undefined) {
325 if (value === undefined) {
209 value = true;
326 value = true;
210 }
327 }
211 if (this.dirty == value) {
328 if (this.dirty == value) {
212 return;
329 return;
213 }
330 }
214 $([IPython.events]).trigger('set_dirty.Notebook', {value: value});
331 $([Events]).trigger('set_dirty.Notebook', {value: value});
215 };
332 };
216
333
217 /**
334 /**
218 * Scroll the top of the page to a given cell.
335 * Scroll the top of the page to a given cell.
219 *
336 *
220 * @method scroll_to_cell
337 * @method scroll_to_cell
221 * @param {Number} cell_number An index of the cell to view
338 * @param {Number} cell_number An index of the cell to view
222 * @param {Number} time Animation time in milliseconds
339 * @param {Number} time Animation time in milliseconds
223 * @return {Number} Pixel offset from the top of the container
340 * @return {Number} Pixel offset from the top of the container
224 */
341 */
225 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
342 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
226 var cells = this.get_cells();
343 var cells = this.get_cells();
227 time = time || 0;
344 time = time || 0;
228 cell_number = Math.min(cells.length-1,cell_number);
345 cell_number = Math.min(cells.length-1,cell_number);
229 cell_number = Math.max(0 ,cell_number);
346 cell_number = Math.max(0 ,cell_number);
230 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
347 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
231 this.element.animate({scrollTop:scroll_value}, time);
348 this.element.animate({scrollTop:scroll_value}, time);
232 return scroll_value;
349 return scroll_value;
233 };
350 };
234
351
235 /**
352 /**
236 * Scroll to the bottom of the page.
353 * Scroll to the bottom of the page.
237 *
354 *
238 * @method scroll_to_bottom
355 * @method scroll_to_bottom
239 */
356 */
240 Notebook.prototype.scroll_to_bottom = function () {
357 Notebook.prototype.scroll_to_bottom = function () {
241 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
358 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
242 };
359 };
243
360
244 /**
361 /**
245 * Scroll to the top of the page.
362 * Scroll to the top of the page.
246 *
363 *
247 * @method scroll_to_top
364 * @method scroll_to_top
248 */
365 */
249 Notebook.prototype.scroll_to_top = function () {
366 Notebook.prototype.scroll_to_top = function () {
250 this.element.animate({scrollTop:0}, 0);
367 this.element.animate({scrollTop:0}, 0);
251 };
368 };
252
369
253 // Edit Notebook metadata
370 // Edit Notebook metadata
254
371
255 Notebook.prototype.edit_metadata = function () {
372 Notebook.prototype.edit_metadata = function () {
256 var that = this;
373 var that = this;
257 IPython.dialog.edit_metadata(this.metadata, function (md) {
374 Dialog.edit_metadata(this.metadata, function (md) {
258 that.metadata = md;
375 that.metadata = md;
259 }, 'Notebook');
376 }, 'Notebook');
260 };
377 };
261
378
262 // Cell indexing, retrieval, etc.
379 // Cell indexing, retrieval, etc.
263
380
264 /**
381 /**
265 * Get all cell elements in the notebook.
382 * Get all cell elements in the notebook.
266 *
383 *
267 * @method get_cell_elements
384 * @method get_cell_elements
268 * @return {jQuery} A selector of all cell elements
385 * @return {jQuery} A selector of all cell elements
269 */
386 */
270 Notebook.prototype.get_cell_elements = function () {
387 Notebook.prototype.get_cell_elements = function () {
271 return this.container.children("div.cell");
388 return this.container.children("div.cell");
272 };
389 };
273
390
274 /**
391 /**
275 * Get a particular cell element.
392 * Get a particular cell element.
276 *
393 *
277 * @method get_cell_element
394 * @method get_cell_element
278 * @param {Number} index An index of a cell to select
395 * @param {Number} index An index of a cell to select
279 * @return {jQuery} A selector of the given cell.
396 * @return {jQuery} A selector of the given cell.
280 */
397 */
281 Notebook.prototype.get_cell_element = function (index) {
398 Notebook.prototype.get_cell_element = function (index) {
282 var result = null;
399 var result = null;
283 var e = this.get_cell_elements().eq(index);
400 var e = this.get_cell_elements().eq(index);
284 if (e.length !== 0) {
401 if (e.length !== 0) {
285 result = e;
402 result = e;
286 }
403 }
287 return result;
404 return result;
288 };
405 };
289
406
290 /**
407 /**
291 * Try to get a particular cell by msg_id.
408 * Try to get a particular cell by msg_id.
292 *
409 *
293 * @method get_msg_cell
410 * @method get_msg_cell
294 * @param {String} msg_id A message UUID
411 * @param {String} msg_id A message UUID
295 * @return {Cell} Cell or null if no cell was found.
412 * @return {Cell} Cell or null if no cell was found.
296 */
413 */
297 Notebook.prototype.get_msg_cell = function (msg_id) {
414 Notebook.prototype.get_msg_cell = function (msg_id) {
298 return IPython.CodeCell.msg_cells[msg_id] || null;
415 return CodeCell.msg_cells[msg_id] || null;
299 };
416 };
300
417
301 /**
418 /**
302 * Count the cells in this notebook.
419 * Count the cells in this notebook.
303 *
420 *
304 * @method ncells
421 * @method ncells
305 * @return {Number} The number of cells in this notebook
422 * @return {Number} The number of cells in this notebook
306 */
423 */
307 Notebook.prototype.ncells = function () {
424 Notebook.prototype.ncells = function () {
308 return this.get_cell_elements().length;
425 return this.get_cell_elements().length;
309 };
426 };
310
427
311 /**
428 /**
312 * Get all Cell objects in this notebook.
429 * Get all Cell objects in this notebook.
313 *
430 *
314 * @method get_cells
431 * @method get_cells
315 * @return {Array} This notebook's Cell objects
432 * @return {Array} This notebook's Cell objects
316 */
433 */
317 // TODO: we are often calling cells as cells()[i], which we should optimize
434 // TODO: we are often calling cells as cells()[i], which we should optimize
318 // to cells(i) or a new method.
435 // to cells(i) or a new method.
319 Notebook.prototype.get_cells = function () {
436 Notebook.prototype.get_cells = function () {
320 return this.get_cell_elements().toArray().map(function (e) {
437 return this.get_cell_elements().toArray().map(function (e) {
321 return $(e).data("cell");
438 return $(e).data("cell");
322 });
439 });
323 };
440 };
324
441
325 /**
442 /**
326 * Get a Cell object from this notebook.
443 * Get a Cell object from this notebook.
327 *
444 *
328 * @method get_cell
445 * @method get_cell
329 * @param {Number} index An index of a cell to retrieve
446 * @param {Number} index An index of a cell to retrieve
330 * @return {Cell} A particular cell
447 * @return {Cell} A particular cell
331 */
448 */
332 Notebook.prototype.get_cell = function (index) {
449 Notebook.prototype.get_cell = function (index) {
333 var result = null;
450 var result = null;
334 var ce = this.get_cell_element(index);
451 var ce = this.get_cell_element(index);
335 if (ce !== null) {
452 if (ce !== null) {
336 result = ce.data('cell');
453 result = ce.data('cell');
337 }
454 }
338 return result;
455 return result;
339 };
456 };
340
457
341 /**
458 /**
342 * Get the cell below a given cell.
459 * Get the cell below a given cell.
343 *
460 *
344 * @method get_next_cell
461 * @method get_next_cell
345 * @param {Cell} cell The provided cell
462 * @param {Cell} cell The provided cell
346 * @return {Cell} The next cell
463 * @return {Cell} The next cell
347 */
464 */
348 Notebook.prototype.get_next_cell = function (cell) {
465 Notebook.prototype.get_next_cell = function (cell) {
349 var result = null;
466 var result = null;
350 var index = this.find_cell_index(cell);
467 var index = this.find_cell_index(cell);
351 if (this.is_valid_cell_index(index+1)) {
468 if (this.is_valid_cell_index(index+1)) {
352 result = this.get_cell(index+1);
469 result = this.get_cell(index+1);
353 }
470 }
354 return result;
471 return result;
355 };
472 };
356
473
357 /**
474 /**
358 * Get the cell above a given cell.
475 * Get the cell above a given cell.
359 *
476 *
360 * @method get_prev_cell
477 * @method get_prev_cell
361 * @param {Cell} cell The provided cell
478 * @param {Cell} cell The provided cell
362 * @return {Cell} The previous cell
479 * @return {Cell} The previous cell
363 */
480 */
364 Notebook.prototype.get_prev_cell = function (cell) {
481 Notebook.prototype.get_prev_cell = function (cell) {
365 // TODO: off-by-one
482 // TODO: off-by-one
366 // nb.get_prev_cell(nb.get_cell(1)) is null
483 // nb.get_prev_cell(nb.get_cell(1)) is null
367 var result = null;
484 var result = null;
368 var index = this.find_cell_index(cell);
485 var index = this.find_cell_index(cell);
369 if (index !== null && index > 1) {
486 if (index !== null && index > 1) {
370 result = this.get_cell(index-1);
487 result = this.get_cell(index-1);
371 }
488 }
372 return result;
489 return result;
373 };
490 };
374
491
375 /**
492 /**
376 * Get the numeric index of a given cell.
493 * Get the numeric index of a given cell.
377 *
494 *
378 * @method find_cell_index
495 * @method find_cell_index
379 * @param {Cell} cell The provided cell
496 * @param {Cell} cell The provided cell
380 * @return {Number} The cell's numeric index
497 * @return {Number} The cell's numeric index
381 */
498 */
382 Notebook.prototype.find_cell_index = function (cell) {
499 Notebook.prototype.find_cell_index = function (cell) {
383 var result = null;
500 var result = null;
384 this.get_cell_elements().filter(function (index) {
501 this.get_cell_elements().filter(function (index) {
385 if ($(this).data("cell") === cell) {
502 if ($(this).data("cell") === cell) {
386 result = index;
503 result = index;
387 }
504 }
388 });
505 });
389 return result;
506 return result;
390 };
507 };
391
508
392 /**
509 /**
393 * Get a given index , or the selected index if none is provided.
510 * Get a given index , or the selected index if none is provided.
394 *
511 *
395 * @method index_or_selected
512 * @method index_or_selected
396 * @param {Number} index A cell's index
513 * @param {Number} index A cell's index
397 * @return {Number} The given index, or selected index if none is provided.
514 * @return {Number} The given index, or selected index if none is provided.
398 */
515 */
399 Notebook.prototype.index_or_selected = function (index) {
516 Notebook.prototype.index_or_selected = function (index) {
400 var i;
517 var i;
401 if (index === undefined || index === null) {
518 if (index === undefined || index === null) {
402 i = this.get_selected_index();
519 i = this.get_selected_index();
403 if (i === null) {
520 if (i === null) {
404 i = 0;
521 i = 0;
405 }
522 }
406 } else {
523 } else {
407 i = index;
524 i = index;
408 }
525 }
409 return i;
526 return i;
410 };
527 };
411
528
412 /**
529 /**
413 * Get the currently selected cell.
530 * Get the currently selected cell.
414 * @method get_selected_cell
531 * @method get_selected_cell
415 * @return {Cell} The selected cell
532 * @return {Cell} The selected cell
416 */
533 */
417 Notebook.prototype.get_selected_cell = function () {
534 Notebook.prototype.get_selected_cell = function () {
418 var index = this.get_selected_index();
535 var index = this.get_selected_index();
419 return this.get_cell(index);
536 return this.get_cell(index);
420 };
537 };
421
538
422 /**
539 /**
423 * Check whether a cell index is valid.
540 * Check whether a cell index is valid.
424 *
541 *
425 * @method is_valid_cell_index
542 * @method is_valid_cell_index
426 * @param {Number} index A cell index
543 * @param {Number} index A cell index
427 * @return True if the index is valid, false otherwise
544 * @return True if the index is valid, false otherwise
428 */
545 */
429 Notebook.prototype.is_valid_cell_index = function (index) {
546 Notebook.prototype.is_valid_cell_index = function (index) {
430 if (index !== null && index >= 0 && index < this.ncells()) {
547 if (index !== null && index >= 0 && index < this.ncells()) {
431 return true;
548 return true;
432 } else {
549 } else {
433 return false;
550 return false;
434 }
551 }
435 };
552 };
436
553
437 /**
554 /**
438 * Get the index of the currently selected cell.
555 * Get the index of the currently selected cell.
439
556
440 * @method get_selected_index
557 * @method get_selected_index
441 * @return {Number} The selected cell's numeric index
558 * @return {Number} The selected cell's numeric index
442 */
559 */
443 Notebook.prototype.get_selected_index = function () {
560 Notebook.prototype.get_selected_index = function () {
444 var result = null;
561 var result = null;
445 this.get_cell_elements().filter(function (index) {
562 this.get_cell_elements().filter(function (index) {
446 if ($(this).data("cell").selected === true) {
563 if ($(this).data("cell").selected === true) {
447 result = index;
564 result = index;
448 }
565 }
449 });
566 });
450 return result;
567 return result;
451 };
568 };
452
569
453
570
454 // Cell selection.
571 // Cell selection.
455
572
456 /**
573 /**
457 * Programmatically select a cell.
574 * Programmatically select a cell.
458 *
575 *
459 * @method select
576 * @method select
460 * @param {Number} index A cell's index
577 * @param {Number} index A cell's index
461 * @return {Notebook} This notebook
578 * @return {Notebook} This notebook
462 */
579 */
463 Notebook.prototype.select = function (index) {
580 Notebook.prototype.select = function (index) {
464 if (this.is_valid_cell_index(index)) {
581 if (this.is_valid_cell_index(index)) {
465 var sindex = this.get_selected_index();
582 var sindex = this.get_selected_index();
466 if (sindex !== null && index !== sindex) {
583 if (sindex !== null && index !== sindex) {
467 // If we are about to select a different cell, make sure we are
584 // If we are about to select a different cell, make sure we are
468 // first in command mode.
585 // first in command mode.
469 if (this.mode !== 'command') {
586 if (this.mode !== 'command') {
470 this.command_mode();
587 this.command_mode();
471 }
588 }
472 this.get_cell(sindex).unselect();
589 this.get_cell(sindex).unselect();
473 }
590 }
474 var cell = this.get_cell(index);
591 var cell = this.get_cell(index);
475 cell.select();
592 cell.select();
476 if (cell.cell_type === 'heading') {
593 if (cell.cell_type === 'heading') {
477 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
594 $([Events]).trigger('selected_cell_type_changed.Notebook',
478 {'cell_type':cell.cell_type,level:cell.level}
595 {'cell_type':cell.cell_type,level:cell.level}
479 );
596 );
480 } else {
597 } else {
481 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
598 $([Events]).trigger('selected_cell_type_changed.Notebook',
482 {'cell_type':cell.cell_type}
599 {'cell_type':cell.cell_type}
483 );
600 );
484 }
601 }
485 }
602 }
486 return this;
603 return this;
487 };
604 };
488
605
489 /**
606 /**
490 * Programmatically select the next cell.
607 * Programmatically select the next cell.
491 *
608 *
492 * @method select_next
609 * @method select_next
493 * @return {Notebook} This notebook
610 * @return {Notebook} This notebook
494 */
611 */
495 Notebook.prototype.select_next = function () {
612 Notebook.prototype.select_next = function () {
496 var index = this.get_selected_index();
613 var index = this.get_selected_index();
497 this.select(index+1);
614 this.select(index+1);
498 return this;
615 return this;
499 };
616 };
500
617
501 /**
618 /**
502 * Programmatically select the previous cell.
619 * Programmatically select the previous cell.
503 *
620 *
504 * @method select_prev
621 * @method select_prev
505 * @return {Notebook} This notebook
622 * @return {Notebook} This notebook
506 */
623 */
507 Notebook.prototype.select_prev = function () {
624 Notebook.prototype.select_prev = function () {
508 var index = this.get_selected_index();
625 var index = this.get_selected_index();
509 this.select(index-1);
626 this.select(index-1);
510 return this;
627 return this;
511 };
628 };
512
629
513
630
514 // Edit/Command mode
631 // Edit/Command mode
515
632
516 /**
633 /**
517 * Gets the index of the cell that is in edit mode.
634 * Gets the index of the cell that is in edit mode.
518 *
635 *
519 * @method get_edit_index
636 * @method get_edit_index
520 *
637 *
521 * @return index {int}
638 * @return index {int}
522 **/
639 **/
523 Notebook.prototype.get_edit_index = function () {
640 Notebook.prototype.get_edit_index = function () {
524 var result = null;
641 var result = null;
525 this.get_cell_elements().filter(function (index) {
642 this.get_cell_elements().filter(function (index) {
526 if ($(this).data("cell").mode === 'edit') {
643 if ($(this).data("cell").mode === 'edit') {
527 result = index;
644 result = index;
528 }
645 }
529 });
646 });
530 return result;
647 return result;
531 };
648 };
532
649
533 /**
650 /**
534 * Handle when a a cell blurs and the notebook should enter command mode.
651 * Handle when a a cell blurs and the notebook should enter command mode.
535 *
652 *
536 * @method handle_command_mode
653 * @method handle_command_mode
537 * @param [cell] {Cell} Cell to enter command mode on.
654 * @param [cell] {Cell} Cell to enter command mode on.
538 **/
655 **/
539 Notebook.prototype.handle_command_mode = function (cell) {
656 Notebook.prototype.handle_command_mode = function (cell) {
540 if (this.mode !== 'command') {
657 if (this.mode !== 'command') {
541 cell.command_mode();
658 cell.command_mode();
542 this.mode = 'command';
659 this.mode = 'command';
543 $([IPython.events]).trigger('command_mode.Notebook');
660 $([Events]).trigger('command_mode.Notebook');
544 IPython.keyboard_manager.command_mode();
661 keyboard_manager.command_mode();
545 }
662 }
546 };
663 };
547
664
548 /**
665 /**
549 * Make the notebook enter command mode.
666 * Make the notebook enter command mode.
550 *
667 *
551 * @method command_mode
668 * @method command_mode
552 **/
669 **/
553 Notebook.prototype.command_mode = function () {
670 Notebook.prototype.command_mode = function () {
554 var cell = this.get_cell(this.get_edit_index());
671 var cell = this.get_cell(this.get_edit_index());
555 if (cell && this.mode !== 'command') {
672 if (cell && this.mode !== 'command') {
556 // We don't call cell.command_mode, but rather call cell.focus_cell()
673 // We don't call cell.command_mode, but rather call cell.focus_cell()
557 // which will blur and CM editor and trigger the call to
674 // which will blur and CM editor and trigger the call to
558 // handle_command_mode.
675 // handle_command_mode.
559 cell.focus_cell();
676 cell.focus_cell();
560 }
677 }
561 };
678 };
562
679
563 /**
680 /**
564 * Handle when a cell fires it's edit_mode event.
681 * Handle when a cell fires it's edit_mode event.
565 *
682 *
566 * @method handle_edit_mode
683 * @method handle_edit_mode
567 * @param [cell] {Cell} Cell to enter edit mode on.
684 * @param [cell] {Cell} Cell to enter edit mode on.
568 **/
685 **/
569 Notebook.prototype.handle_edit_mode = function (cell) {
686 Notebook.prototype.handle_edit_mode = function (cell) {
570 if (cell && this.mode !== 'edit') {
687 if (cell && this.mode !== 'edit') {
571 cell.edit_mode();
688 cell.edit_mode();
572 this.mode = 'edit';
689 this.mode = 'edit';
573 $([IPython.events]).trigger('edit_mode.Notebook');
690 $([Events]).trigger('edit_mode.Notebook');
574 IPython.keyboard_manager.edit_mode();
691 keyboard_manager.edit_mode();
575 }
692 }
576 };
693 };
577
694
578 /**
695 /**
579 * Make a cell enter edit mode.
696 * Make a cell enter edit mode.
580 *
697 *
581 * @method edit_mode
698 * @method edit_mode
582 **/
699 **/
583 Notebook.prototype.edit_mode = function () {
700 Notebook.prototype.edit_mode = function () {
584 var cell = this.get_selected_cell();
701 var cell = this.get_selected_cell();
585 if (cell && this.mode !== 'edit') {
702 if (cell && this.mode !== 'edit') {
586 cell.unrender();
703 cell.unrender();
587 cell.focus_editor();
704 cell.focus_editor();
588 }
705 }
589 };
706 };
590
707
591 /**
708 /**
592 * Focus the currently selected cell.
709 * Focus the currently selected cell.
593 *
710 *
594 * @method focus_cell
711 * @method focus_cell
595 **/
712 **/
596 Notebook.prototype.focus_cell = function () {
713 Notebook.prototype.focus_cell = function () {
597 var cell = this.get_selected_cell();
714 var cell = this.get_selected_cell();
598 if (cell === null) {return;} // No cell is selected
715 if (cell === null) {return;} // No cell is selected
599 cell.focus_cell();
716 cell.focus_cell();
600 };
717 };
601
718
602 // Cell movement
719 // Cell movement
603
720
604 /**
721 /**
605 * Move given (or selected) cell up and select it.
722 * Move given (or selected) cell up and select it.
606 *
723 *
607 * @method move_cell_up
724 * @method move_cell_up
608 * @param [index] {integer} cell index
725 * @param [index] {integer} cell index
609 * @return {Notebook} This notebook
726 * @return {Notebook} This notebook
610 **/
727 **/
611 Notebook.prototype.move_cell_up = function (index) {
728 Notebook.prototype.move_cell_up = function (index) {
612 var i = this.index_or_selected(index);
729 var i = this.index_or_selected(index);
613 if (this.is_valid_cell_index(i) && i > 0) {
730 if (this.is_valid_cell_index(i) && i > 0) {
614 var pivot = this.get_cell_element(i-1);
731 var pivot = this.get_cell_element(i-1);
615 var tomove = this.get_cell_element(i);
732 var tomove = this.get_cell_element(i);
616 if (pivot !== null && tomove !== null) {
733 if (pivot !== null && tomove !== null) {
617 tomove.detach();
734 tomove.detach();
618 pivot.before(tomove);
735 pivot.before(tomove);
619 this.select(i-1);
736 this.select(i-1);
620 var cell = this.get_selected_cell();
737 var cell = this.get_selected_cell();
621 cell.focus_cell();
738 cell.focus_cell();
622 }
739 }
623 this.set_dirty(true);
740 this.set_dirty(true);
624 }
741 }
625 return this;
742 return this;
626 };
743 };
627
744
628
745
629 /**
746 /**
630 * Move given (or selected) cell down and select it
747 * Move given (or selected) cell down and select it
631 *
748 *
632 * @method move_cell_down
749 * @method move_cell_down
633 * @param [index] {integer} cell index
750 * @param [index] {integer} cell index
634 * @return {Notebook} This notebook
751 * @return {Notebook} This notebook
635 **/
752 **/
636 Notebook.prototype.move_cell_down = function (index) {
753 Notebook.prototype.move_cell_down = function (index) {
637 var i = this.index_or_selected(index);
754 var i = this.index_or_selected(index);
638 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
755 if (this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
639 var pivot = this.get_cell_element(i+1);
756 var pivot = this.get_cell_element(i+1);
640 var tomove = this.get_cell_element(i);
757 var tomove = this.get_cell_element(i);
641 if (pivot !== null && tomove !== null) {
758 if (pivot !== null && tomove !== null) {
642 tomove.detach();
759 tomove.detach();
643 pivot.after(tomove);
760 pivot.after(tomove);
644 this.select(i+1);
761 this.select(i+1);
645 var cell = this.get_selected_cell();
762 var cell = this.get_selected_cell();
646 cell.focus_cell();
763 cell.focus_cell();
647 }
764 }
648 }
765 }
649 this.set_dirty();
766 this.set_dirty();
650 return this;
767 return this;
651 };
768 };
652
769
653
770
654 // Insertion, deletion.
771 // Insertion, deletion.
655
772
656 /**
773 /**
657 * Delete a cell from the notebook.
774 * Delete a cell from the notebook.
658 *
775 *
659 * @method delete_cell
776 * @method delete_cell
660 * @param [index] A cell's numeric index
777 * @param [index] A cell's numeric index
661 * @return {Notebook} This notebook
778 * @return {Notebook} This notebook
662 */
779 */
663 Notebook.prototype.delete_cell = function (index) {
780 Notebook.prototype.delete_cell = function (index) {
664 var i = this.index_or_selected(index);
781 var i = this.index_or_selected(index);
665 var cell = this.get_selected_cell();
782 var cell = this.get_selected_cell();
666 this.undelete_backup = cell.toJSON();
783 this.undelete_backup = cell.toJSON();
667 $('#undelete_cell').removeClass('disabled');
784 $('#undelete_cell').removeClass('disabled');
668 if (this.is_valid_cell_index(i)) {
785 if (this.is_valid_cell_index(i)) {
669 var old_ncells = this.ncells();
786 var old_ncells = this.ncells();
670 var ce = this.get_cell_element(i);
787 var ce = this.get_cell_element(i);
671 ce.remove();
788 ce.remove();
672 if (i === 0) {
789 if (i === 0) {
673 // Always make sure we have at least one cell.
790 // Always make sure we have at least one cell.
674 if (old_ncells === 1) {
791 if (old_ncells === 1) {
675 this.insert_cell_below('code');
792 this.insert_cell_below('code');
676 }
793 }
677 this.select(0);
794 this.select(0);
678 this.undelete_index = 0;
795 this.undelete_index = 0;
679 this.undelete_below = false;
796 this.undelete_below = false;
680 } else if (i === old_ncells-1 && i !== 0) {
797 } else if (i === old_ncells-1 && i !== 0) {
681 this.select(i-1);
798 this.select(i-1);
682 this.undelete_index = i - 1;
799 this.undelete_index = i - 1;
683 this.undelete_below = true;
800 this.undelete_below = true;
684 } else {
801 } else {
685 this.select(i);
802 this.select(i);
686 this.undelete_index = i;
803 this.undelete_index = i;
687 this.undelete_below = false;
804 this.undelete_below = false;
688 }
805 }
689 $([IPython.events]).trigger('delete.Cell', {'cell': cell, 'index': i});
806 $([Events]).trigger('delete.Cell', {'cell': cell, 'index': i});
690 this.set_dirty(true);
807 this.set_dirty(true);
691 }
808 }
692 return this;
809 return this;
693 };
810 };
694
811
695 /**
812 /**
696 * Restore the most recently deleted cell.
813 * Restore the most recently deleted cell.
697 *
814 *
698 * @method undelete
815 * @method undelete
699 */
816 */
700 Notebook.prototype.undelete_cell = function() {
817 Notebook.prototype.undelete_cell = function() {
701 if (this.undelete_backup !== null && this.undelete_index !== null) {
818 if (this.undelete_backup !== null && this.undelete_index !== null) {
702 var current_index = this.get_selected_index();
819 var current_index = this.get_selected_index();
703 if (this.undelete_index < current_index) {
820 if (this.undelete_index < current_index) {
704 current_index = current_index + 1;
821 current_index = current_index + 1;
705 }
822 }
706 if (this.undelete_index >= this.ncells()) {
823 if (this.undelete_index >= this.ncells()) {
707 this.select(this.ncells() - 1);
824 this.select(this.ncells() - 1);
708 }
825 }
709 else {
826 else {
710 this.select(this.undelete_index);
827 this.select(this.undelete_index);
711 }
828 }
712 var cell_data = this.undelete_backup;
829 var cell_data = this.undelete_backup;
713 var new_cell = null;
830 var new_cell = null;
714 if (this.undelete_below) {
831 if (this.undelete_below) {
715 new_cell = this.insert_cell_below(cell_data.cell_type);
832 new_cell = this.insert_cell_below(cell_data.cell_type);
716 } else {
833 } else {
717 new_cell = this.insert_cell_above(cell_data.cell_type);
834 new_cell = this.insert_cell_above(cell_data.cell_type);
718 }
835 }
719 new_cell.fromJSON(cell_data);
836 new_cell.fromJSON(cell_data);
720 if (this.undelete_below) {
837 if (this.undelete_below) {
721 this.select(current_index+1);
838 this.select(current_index+1);
722 } else {
839 } else {
723 this.select(current_index);
840 this.select(current_index);
724 }
841 }
725 this.undelete_backup = null;
842 this.undelete_backup = null;
726 this.undelete_index = null;
843 this.undelete_index = null;
727 }
844 }
728 $('#undelete_cell').addClass('disabled');
845 $('#undelete_cell').addClass('disabled');
729 };
846 };
730
847
731 /**
848 /**
732 * Insert a cell so that after insertion the cell is at given index.
849 * Insert a cell so that after insertion the cell is at given index.
733 *
850 *
734 * If cell type is not provided, it will default to the type of the
851 * If cell type is not provided, it will default to the type of the
735 * currently active cell.
852 * currently active cell.
736 *
853 *
737 * Similar to insert_above, but index parameter is mandatory
854 * Similar to insert_above, but index parameter is mandatory
738 *
855 *
739 * Index will be brought back into the accessible range [0,n]
856 * Index will be brought back into the accessible range [0,n]
740 *
857 *
741 * @method insert_cell_at_index
858 * @method insert_cell_at_index
742 * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
859 * @param [type] {string} in ['code','markdown','heading'], defaults to 'code'
743 * @param [index] {int} a valid index where to insert cell
860 * @param [index] {int} a valid index where to insert cell
744 *
861 *
745 * @return cell {cell|null} created cell or null
862 * @return cell {cell|null} created cell or null
746 **/
863 **/
747 Notebook.prototype.insert_cell_at_index = function(type, index){
864 Notebook.prototype.insert_cell_at_index = function(type, index){
748
865
749 var ncells = this.ncells();
866 var ncells = this.ncells();
750 index = Math.min(index,ncells);
867 index = Math.min(index,ncells);
751 index = Math.max(index,0);
868 index = Math.max(index,0);
752 var cell = null;
869 var cell = null;
753 type = type || this.get_selected_cell().cell_type;
870 type = type || this.get_selected_cell().cell_type;
754
871
755 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
872 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
756 if (type === 'code') {
873 if (type === 'code') {
757 cell = new IPython.CodeCell(this.kernel);
874 cell = new CodeCell(this.kernel);
758 cell.set_input_prompt();
875 cell.set_input_prompt();
759 } else if (type === 'markdown') {
876 } else if (type === 'markdown') {
760 cell = new IPython.MarkdownCell();
877 cell = new Cells.MarkdownCell();
761 } else if (type === 'raw') {
878 } else if (type === 'raw') {
762 cell = new IPython.RawCell();
879 cell = new Cells.RawCell();
763 } else if (type === 'heading') {
880 } else if (type === 'heading') {
764 cell = new IPython.HeadingCell();
881 cell = new Cells.HeadingCell();
765 }
882 }
766
883
767 if(this._insert_element_at_index(cell.element,index)) {
884 if(this._insert_element_at_index(cell.element,index)) {
768 cell.render();
885 cell.render();
769 $([IPython.events]).trigger('create.Cell', {'cell': cell, 'index': index});
886 $([Events]).trigger('create.Cell', {'cell': cell, 'index': index});
770 cell.refresh();
887 cell.refresh();
771 // We used to select the cell after we refresh it, but there
888 // We used to select the cell after we refresh it, but there
772 // are now cases were this method is called where select is
889 // are now cases were this method is called where select is
773 // not appropriate. The selection logic should be handled by the
890 // not appropriate. The selection logic should be handled by the
774 // caller of the the top level insert_cell methods.
891 // caller of the the top level insert_cell methods.
775 this.set_dirty(true);
892 this.set_dirty(true);
776 }
893 }
777 }
894 }
778 return cell;
895 return cell;
779
896
780 };
897 };
781
898
782 /**
899 /**
783 * Insert an element at given cell index.
900 * Insert an element at given cell index.
784 *
901 *
785 * @method _insert_element_at_index
902 * @method _insert_element_at_index
786 * @param element {dom element} a cell element
903 * @param element {dom element} a cell element
787 * @param [index] {int} a valid index where to inser cell
904 * @param [index] {int} a valid index where to inser cell
788 * @private
905 * @private
789 *
906 *
790 * return true if everything whent fine.
907 * return true if everything whent fine.
791 **/
908 **/
792 Notebook.prototype._insert_element_at_index = function(element, index){
909 Notebook.prototype._insert_element_at_index = function(element, index){
793 if (element === undefined){
910 if (element === undefined){
794 return false;
911 return false;
795 }
912 }
796
913
797 var ncells = this.ncells();
914 var ncells = this.ncells();
798
915
799 if (ncells === 0) {
916 if (ncells === 0) {
800 // special case append if empty
917 // special case append if empty
801 this.element.find('div.end_space').before(element);
918 this.element.find('div.end_space').before(element);
802 } else if ( ncells === index ) {
919 } else if ( ncells === index ) {
803 // special case append it the end, but not empty
920 // special case append it the end, but not empty
804 this.get_cell_element(index-1).after(element);
921 this.get_cell_element(index-1).after(element);
805 } else if (this.is_valid_cell_index(index)) {
922 } else if (this.is_valid_cell_index(index)) {
806 // otherwise always somewhere to append to
923 // otherwise always somewhere to append to
807 this.get_cell_element(index).before(element);
924 this.get_cell_element(index).before(element);
808 } else {
925 } else {
809 return false;
926 return false;
810 }
927 }
811
928
812 if (this.undelete_index !== null && index <= this.undelete_index) {
929 if (this.undelete_index !== null && index <= this.undelete_index) {
813 this.undelete_index = this.undelete_index + 1;
930 this.undelete_index = this.undelete_index + 1;
814 this.set_dirty(true);
931 this.set_dirty(true);
815 }
932 }
816 return true;
933 return true;
817 };
934 };
818
935
819 /**
936 /**
820 * Insert a cell of given type above given index, or at top
937 * Insert a cell of given type above given index, or at top
821 * of notebook if index smaller than 0.
938 * of notebook if index smaller than 0.
822 *
939 *
823 * default index value is the one of currently selected cell
940 * default index value is the one of currently selected cell
824 *
941 *
825 * @method insert_cell_above
942 * @method insert_cell_above
826 * @param [type] {string} cell type
943 * @param [type] {string} cell type
827 * @param [index] {integer}
944 * @param [index] {integer}
828 *
945 *
829 * @return handle to created cell or null
946 * @return handle to created cell or null
830 **/
947 **/
831 Notebook.prototype.insert_cell_above = function (type, index) {
948 Notebook.prototype.insert_cell_above = function (type, index) {
832 index = this.index_or_selected(index);
949 index = this.index_or_selected(index);
833 return this.insert_cell_at_index(type, index);
950 return this.insert_cell_at_index(type, index);
834 };
951 };
835
952
836 /**
953 /**
837 * Insert a cell of given type below given index, or at bottom
954 * Insert a cell of given type below given index, or at bottom
838 * of notebook if index greater than number of cells
955 * of notebook if index greater than number of cells
839 *
956 *
840 * default index value is the one of currently selected cell
957 * default index value is the one of currently selected cell
841 *
958 *
842 * @method insert_cell_below
959 * @method insert_cell_below
843 * @param [type] {string} cell type
960 * @param [type] {string} cell type
844 * @param [index] {integer}
961 * @param [index] {integer}
845 *
962 *
846 * @return handle to created cell or null
963 * @return handle to created cell or null
847 *
964 *
848 **/
965 **/
849 Notebook.prototype.insert_cell_below = function (type, index) {
966 Notebook.prototype.insert_cell_below = function (type, index) {
850 index = this.index_or_selected(index);
967 index = this.index_or_selected(index);
851 return this.insert_cell_at_index(type, index+1);
968 return this.insert_cell_at_index(type, index+1);
852 };
969 };
853
970
854
971
855 /**
972 /**
856 * Insert cell at end of notebook
973 * Insert cell at end of notebook
857 *
974 *
858 * @method insert_cell_at_bottom
975 * @method insert_cell_at_bottom
859 * @param {String} type cell type
976 * @param {String} type cell type
860 *
977 *
861 * @return the added cell; or null
978 * @return the added cell; or null
862 **/
979 **/
863 Notebook.prototype.insert_cell_at_bottom = function (type){
980 Notebook.prototype.insert_cell_at_bottom = function (type){
864 var len = this.ncells();
981 var len = this.ncells();
865 return this.insert_cell_below(type,len-1);
982 return this.insert_cell_below(type,len-1);
866 };
983 };
867
984
868 /**
985 /**
869 * Turn a cell into a code cell.
986 * Turn a cell into a code cell.
870 *
987 *
871 * @method to_code
988 * @method to_code
872 * @param {Number} [index] A cell's index
989 * @param {Number} [index] A cell's index
873 */
990 */
874 Notebook.prototype.to_code = function (index) {
991 Notebook.prototype.to_code = function (index) {
875 var i = this.index_or_selected(index);
992 var i = this.index_or_selected(index);
876 if (this.is_valid_cell_index(i)) {
993 if (this.is_valid_cell_index(i)) {
877 var source_element = this.get_cell_element(i);
994 var source_element = this.get_cell_element(i);
878 var source_cell = source_element.data("cell");
995 var source_cell = source_element.data("cell");
879 if (!(source_cell instanceof IPython.CodeCell)) {
996 if (!(source_cell instanceof CodeCell)) {
880 var target_cell = this.insert_cell_below('code',i);
997 var target_cell = this.insert_cell_below('code',i);
881 var text = source_cell.get_text();
998 var text = source_cell.get_text();
882 if (text === source_cell.placeholder) {
999 if (text === source_cell.placeholder) {
883 text = '';
1000 text = '';
884 }
1001 }
885 target_cell.set_text(text);
1002 target_cell.set_text(text);
886 // make this value the starting point, so that we can only undo
1003 // make this value the starting point, so that we can only undo
887 // to this state, instead of a blank cell
1004 // to this state, instead of a blank cell
888 target_cell.code_mirror.clearHistory();
1005 target_cell.code_mirror.clearHistory();
889 source_element.remove();
1006 source_element.remove();
890 this.select(i);
1007 this.select(i);
891 var cursor = source_cell.code_mirror.getCursor();
1008 var cursor = source_cell.code_mirror.getCursor();
892 target_cell.code_mirror.setCursor(cursor);
1009 target_cell.code_mirror.setCursor(cursor);
893 this.set_dirty(true);
1010 this.set_dirty(true);
894 }
1011 }
895 }
1012 }
896 };
1013 };
897
1014
898 /**
1015 /**
899 * Turn a cell into a Markdown cell.
1016 * Turn a cell into a Markdown cell.
900 *
1017 *
901 * @method to_markdown
1018 * @method to_markdown
902 * @param {Number} [index] A cell's index
1019 * @param {Number} [index] A cell's index
903 */
1020 */
904 Notebook.prototype.to_markdown = function (index) {
1021 Notebook.prototype.to_markdown = function (index) {
905 var i = this.index_or_selected(index);
1022 var i = this.index_or_selected(index);
906 if (this.is_valid_cell_index(i)) {
1023 if (this.is_valid_cell_index(i)) {
907 var source_element = this.get_cell_element(i);
1024 var source_element = this.get_cell_element(i);
908 var source_cell = source_element.data("cell");
1025 var source_cell = source_element.data("cell");
909 if (!(source_cell instanceof IPython.MarkdownCell)) {
1026 if (!(source_cell instanceof Cells.MarkdownCell)) {
910 var target_cell = this.insert_cell_below('markdown',i);
1027 var target_cell = this.insert_cell_below('markdown',i);
911 var text = source_cell.get_text();
1028 var text = source_cell.get_text();
912 if (text === source_cell.placeholder) {
1029 if (text === source_cell.placeholder) {
913 text = '';
1030 text = '';
914 }
1031 }
915 // We must show the editor before setting its contents
1032 // We must show the editor before setting its contents
916 target_cell.unrender();
1033 target_cell.unrender();
917 target_cell.set_text(text);
1034 target_cell.set_text(text);
918 // make this value the starting point, so that we can only undo
1035 // make this value the starting point, so that we can only undo
919 // to this state, instead of a blank cell
1036 // to this state, instead of a blank cell
920 target_cell.code_mirror.clearHistory();
1037 target_cell.code_mirror.clearHistory();
921 source_element.remove();
1038 source_element.remove();
922 this.select(i);
1039 this.select(i);
923 if ((source_cell instanceof IPython.TextCell) && source_cell.rendered) {
1040 if ((source_cell instanceof Cells.TextCell) && source_cell.rendered) {
924 target_cell.render();
1041 target_cell.render();
925 }
1042 }
926 var cursor = source_cell.code_mirror.getCursor();
1043 var cursor = source_cell.code_mirror.getCursor();
927 target_cell.code_mirror.setCursor(cursor);
1044 target_cell.code_mirror.setCursor(cursor);
928 this.set_dirty(true);
1045 this.set_dirty(true);
929 }
1046 }
930 }
1047 }
931 };
1048 };
932
1049
933 /**
1050 /**
934 * Turn a cell into a raw text cell.
1051 * Turn a cell into a raw text cell.
935 *
1052 *
936 * @method to_raw
1053 * @method to_raw
937 * @param {Number} [index] A cell's index
1054 * @param {Number} [index] A cell's index
938 */
1055 */
939 Notebook.prototype.to_raw = function (index) {
1056 Notebook.prototype.to_raw = function (index) {
940 var i = this.index_or_selected(index);
1057 var i = this.index_or_selected(index);
941 if (this.is_valid_cell_index(i)) {
1058 if (this.is_valid_cell_index(i)) {
942 var source_element = this.get_cell_element(i);
1059 var source_element = this.get_cell_element(i);
943 var source_cell = source_element.data("cell");
1060 var source_cell = source_element.data("cell");
944 var target_cell = null;
1061 var target_cell = null;
945 if (!(source_cell instanceof IPython.RawCell)) {
1062 if (!(source_cell instanceof Cells.RawCell)) {
946 target_cell = this.insert_cell_below('raw',i);
1063 target_cell = this.insert_cell_below('raw',i);
947 var text = source_cell.get_text();
1064 var text = source_cell.get_text();
948 if (text === source_cell.placeholder) {
1065 if (text === source_cell.placeholder) {
949 text = '';
1066 text = '';
950 }
1067 }
951 // We must show the editor before setting its contents
1068 // We must show the editor before setting its contents
952 target_cell.unrender();
1069 target_cell.unrender();
953 target_cell.set_text(text);
1070 target_cell.set_text(text);
954 // make this value the starting point, so that we can only undo
1071 // make this value the starting point, so that we can only undo
955 // to this state, instead of a blank cell
1072 // to this state, instead of a blank cell
956 target_cell.code_mirror.clearHistory();
1073 target_cell.code_mirror.clearHistory();
957 source_element.remove();
1074 source_element.remove();
958 this.select(i);
1075 this.select(i);
959 var cursor = source_cell.code_mirror.getCursor();
1076 var cursor = source_cell.code_mirror.getCursor();
960 target_cell.code_mirror.setCursor(cursor);
1077 target_cell.code_mirror.setCursor(cursor);
961 this.set_dirty(true);
1078 this.set_dirty(true);
962 }
1079 }
963 }
1080 }
964 };
1081 };
965
1082
966 /**
1083 /**
967 * Turn a cell into a heading cell.
1084 * Turn a cell into a heading cell.
968 *
1085 *
969 * @method to_heading
1086 * @method to_heading
970 * @param {Number} [index] A cell's index
1087 * @param {Number} [index] A cell's index
971 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
1088 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
972 */
1089 */
973 Notebook.prototype.to_heading = function (index, level) {
1090 Notebook.prototype.to_heading = function (index, level) {
974 level = level || 1;
1091 level = level || 1;
975 var i = this.index_or_selected(index);
1092 var i = this.index_or_selected(index);
976 if (this.is_valid_cell_index(i)) {
1093 if (this.is_valid_cell_index(i)) {
977 var source_element = this.get_cell_element(i);
1094 var source_element = this.get_cell_element(i);
978 var source_cell = source_element.data("cell");
1095 var source_cell = source_element.data("cell");
979 var target_cell = null;
1096 var target_cell = null;
980 if (source_cell instanceof IPython.HeadingCell) {
1097 if (source_cell instanceof Cells.HeadingCell) {
981 source_cell.set_level(level);
1098 source_cell.set_level(level);
982 } else {
1099 } else {
983 target_cell = this.insert_cell_below('heading',i);
1100 target_cell = this.insert_cell_below('heading',i);
984 var text = source_cell.get_text();
1101 var text = source_cell.get_text();
985 if (text === source_cell.placeholder) {
1102 if (text === source_cell.placeholder) {
986 text = '';
1103 text = '';
987 }
1104 }
988 // We must show the editor before setting its contents
1105 // We must show the editor before setting its contents
989 target_cell.set_level(level);
1106 target_cell.set_level(level);
990 target_cell.unrender();
1107 target_cell.unrender();
991 target_cell.set_text(text);
1108 target_cell.set_text(text);
992 // make this value the starting point, so that we can only undo
1109 // make this value the starting point, so that we can only undo
993 // to this state, instead of a blank cell
1110 // to this state, instead of a blank cell
994 target_cell.code_mirror.clearHistory();
1111 target_cell.code_mirror.clearHistory();
995 source_element.remove();
1112 source_element.remove();
996 this.select(i);
1113 this.select(i);
997 var cursor = source_cell.code_mirror.getCursor();
1114 var cursor = source_cell.code_mirror.getCursor();
998 target_cell.code_mirror.setCursor(cursor);
1115 target_cell.code_mirror.setCursor(cursor);
999 if ((source_cell instanceof IPython.TextCell) && source_cell.rendered) {
1116 if ((source_cell instanceof Cells.TextCell) && source_cell.rendered) {
1000 target_cell.render();
1117 target_cell.render();
1001 }
1118 }
1002 }
1119 }
1003 this.set_dirty(true);
1120 this.set_dirty(true);
1004 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
1121 $([Events]).trigger('selected_cell_type_changed.Notebook',
1005 {'cell_type':'heading',level:level}
1122 {'cell_type':'heading',level:level}
1006 );
1123 );
1007 }
1124 }
1008 };
1125 };
1009
1126
1010
1127
1011 // Cut/Copy/Paste
1128 // Cut/Copy/Paste
1012
1129
1013 /**
1130 /**
1014 * Enable UI elements for pasting cells.
1131 * Enable UI elements for pasting cells.
1015 *
1132 *
1016 * @method enable_paste
1133 * @method enable_paste
1017 */
1134 */
1018 Notebook.prototype.enable_paste = function () {
1135 Notebook.prototype.enable_paste = function () {
1019 var that = this;
1136 var that = this;
1020 if (!this.paste_enabled) {
1137 if (!this.paste_enabled) {
1021 $('#paste_cell_replace').removeClass('disabled')
1138 $('#paste_cell_replace').removeClass('disabled')
1022 .on('click', function () {that.paste_cell_replace();});
1139 .on('click', function () {that.paste_cell_replace();});
1023 $('#paste_cell_above').removeClass('disabled')
1140 $('#paste_cell_above').removeClass('disabled')
1024 .on('click', function () {that.paste_cell_above();});
1141 .on('click', function () {that.paste_cell_above();});
1025 $('#paste_cell_below').removeClass('disabled')
1142 $('#paste_cell_below').removeClass('disabled')
1026 .on('click', function () {that.paste_cell_below();});
1143 .on('click', function () {that.paste_cell_below();});
1027 this.paste_enabled = true;
1144 this.paste_enabled = true;
1028 }
1145 }
1029 };
1146 };
1030
1147
1031 /**
1148 /**
1032 * Disable UI elements for pasting cells.
1149 * Disable UI elements for pasting cells.
1033 *
1150 *
1034 * @method disable_paste
1151 * @method disable_paste
1035 */
1152 */
1036 Notebook.prototype.disable_paste = function () {
1153 Notebook.prototype.disable_paste = function () {
1037 if (this.paste_enabled) {
1154 if (this.paste_enabled) {
1038 $('#paste_cell_replace').addClass('disabled').off('click');
1155 $('#paste_cell_replace').addClass('disabled').off('click');
1039 $('#paste_cell_above').addClass('disabled').off('click');
1156 $('#paste_cell_above').addClass('disabled').off('click');
1040 $('#paste_cell_below').addClass('disabled').off('click');
1157 $('#paste_cell_below').addClass('disabled').off('click');
1041 this.paste_enabled = false;
1158 this.paste_enabled = false;
1042 }
1159 }
1043 };
1160 };
1044
1161
1045 /**
1162 /**
1046 * Cut a cell.
1163 * Cut a cell.
1047 *
1164 *
1048 * @method cut_cell
1165 * @method cut_cell
1049 */
1166 */
1050 Notebook.prototype.cut_cell = function () {
1167 Notebook.prototype.cut_cell = function () {
1051 this.copy_cell();
1168 this.copy_cell();
1052 this.delete_cell();
1169 this.delete_cell();
1053 };
1170 };
1054
1171
1055 /**
1172 /**
1056 * Copy a cell.
1173 * Copy a cell.
1057 *
1174 *
1058 * @method copy_cell
1175 * @method copy_cell
1059 */
1176 */
1060 Notebook.prototype.copy_cell = function () {
1177 Notebook.prototype.copy_cell = function () {
1061 var cell = this.get_selected_cell();
1178 var cell = this.get_selected_cell();
1062 this.clipboard = cell.toJSON();
1179 this.clipboard = cell.toJSON();
1063 this.enable_paste();
1180 this.enable_paste();
1064 };
1181 };
1065
1182
1066 /**
1183 /**
1067 * Replace the selected cell with a cell in the clipboard.
1184 * Replace the selected cell with a cell in the clipboard.
1068 *
1185 *
1069 * @method paste_cell_replace
1186 * @method paste_cell_replace
1070 */
1187 */
1071 Notebook.prototype.paste_cell_replace = function () {
1188 Notebook.prototype.paste_cell_replace = function () {
1072 if (this.clipboard !== null && this.paste_enabled) {
1189 if (this.clipboard !== null && this.paste_enabled) {
1073 var cell_data = this.clipboard;
1190 var cell_data = this.clipboard;
1074 var new_cell = this.insert_cell_above(cell_data.cell_type);
1191 var new_cell = this.insert_cell_above(cell_data.cell_type);
1075 new_cell.fromJSON(cell_data);
1192 new_cell.fromJSON(cell_data);
1076 var old_cell = this.get_next_cell(new_cell);
1193 var old_cell = this.get_next_cell(new_cell);
1077 this.delete_cell(this.find_cell_index(old_cell));
1194 this.delete_cell(this.find_cell_index(old_cell));
1078 this.select(this.find_cell_index(new_cell));
1195 this.select(this.find_cell_index(new_cell));
1079 }
1196 }
1080 };
1197 };
1081
1198
1082 /**
1199 /**
1083 * Paste a cell from the clipboard above the selected cell.
1200 * Paste a cell from the clipboard above the selected cell.
1084 *
1201 *
1085 * @method paste_cell_above
1202 * @method paste_cell_above
1086 */
1203 */
1087 Notebook.prototype.paste_cell_above = function () {
1204 Notebook.prototype.paste_cell_above = function () {
1088 if (this.clipboard !== null && this.paste_enabled) {
1205 if (this.clipboard !== null && this.paste_enabled) {
1089 var cell_data = this.clipboard;
1206 var cell_data = this.clipboard;
1090 var new_cell = this.insert_cell_above(cell_data.cell_type);
1207 var new_cell = this.insert_cell_above(cell_data.cell_type);
1091 new_cell.fromJSON(cell_data);
1208 new_cell.fromJSON(cell_data);
1092 new_cell.focus_cell();
1209 new_cell.focus_cell();
1093 }
1210 }
1094 };
1211 };
1095
1212
1096 /**
1213 /**
1097 * Paste a cell from the clipboard below the selected cell.
1214 * Paste a cell from the clipboard below the selected cell.
1098 *
1215 *
1099 * @method paste_cell_below
1216 * @method paste_cell_below
1100 */
1217 */
1101 Notebook.prototype.paste_cell_below = function () {
1218 Notebook.prototype.paste_cell_below = function () {
1102 if (this.clipboard !== null && this.paste_enabled) {
1219 if (this.clipboard !== null && this.paste_enabled) {
1103 var cell_data = this.clipboard;
1220 var cell_data = this.clipboard;
1104 var new_cell = this.insert_cell_below(cell_data.cell_type);
1221 var new_cell = this.insert_cell_below(cell_data.cell_type);
1105 new_cell.fromJSON(cell_data);
1222 new_cell.fromJSON(cell_data);
1106 new_cell.focus_cell();
1223 new_cell.focus_cell();
1107 }
1224 }
1108 };
1225 };
1109
1226
1110 // Split/merge
1227 // Split/merge
1111
1228
1112 /**
1229 /**
1113 * Split the selected cell into two, at the cursor.
1230 * Split the selected cell into two, at the cursor.
1114 *
1231 *
1115 * @method split_cell
1232 * @method split_cell
1116 */
1233 */
1117 Notebook.prototype.split_cell = function () {
1234 Notebook.prototype.split_cell = function () {
1118 var mdc = IPython.MarkdownCell;
1235 var mdc = Cells.MarkdownCell;
1119 var rc = IPython.RawCell;
1236 var rc = Cells.RawCell;
1120 var cell = this.get_selected_cell();
1237 var cell = this.get_selected_cell();
1121 if (cell.is_splittable()) {
1238 if (cell.is_splittable()) {
1122 var texta = cell.get_pre_cursor();
1239 var texta = cell.get_pre_cursor();
1123 var textb = cell.get_post_cursor();
1240 var textb = cell.get_post_cursor();
1124 if (cell instanceof IPython.CodeCell) {
1241 if (cell instanceof CodeCell) {
1125 // In this case the operations keep the notebook in its existing mode
1242 // In this case the operations keep the notebook in its existing mode
1126 // so we don't need to do any post-op mode changes.
1243 // so we don't need to do any post-op mode changes.
1127 cell.set_text(textb);
1244 cell.set_text(textb);
1128 var new_cell = this.insert_cell_above('code');
1245 var new_cell = this.insert_cell_above('code');
1129 new_cell.set_text(texta);
1246 new_cell.set_text(texta);
1130 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1247 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1131 // We know cell is !rendered so we can use set_text.
1248 // We know cell is !rendered so we can use set_text.
1132 cell.set_text(textb);
1249 cell.set_text(textb);
1133 var new_cell = this.insert_cell_above(cell.cell_type);
1250 var new_cell = this.insert_cell_above(cell.cell_type);
1134 // Unrender the new cell so we can call set_text.
1251 // Unrender the new cell so we can call set_text.
1135 new_cell.unrender();
1252 new_cell.unrender();
1136 new_cell.set_text(texta);
1253 new_cell.set_text(texta);
1137 }
1254 }
1138 }
1255 }
1139 };
1256 };
1140
1257
1141 /**
1258 /**
1142 * Combine the selected cell into the cell above it.
1259 * Combine the selected cell into the cell above it.
1143 *
1260 *
1144 * @method merge_cell_above
1261 * @method merge_cell_above
1145 */
1262 */
1146 Notebook.prototype.merge_cell_above = function () {
1263 Notebook.prototype.merge_cell_above = function () {
1147 var mdc = IPython.MarkdownCell;
1264 var mdc = Cells.MarkdownCell;
1148 var rc = IPython.RawCell;
1265 var rc = Cells.RawCell;
1149 var index = this.get_selected_index();
1266 var index = this.get_selected_index();
1150 var cell = this.get_cell(index);
1267 var cell = this.get_cell(index);
1151 var render = cell.rendered;
1268 var render = cell.rendered;
1152 if (!cell.is_mergeable()) {
1269 if (!cell.is_mergeable()) {
1153 return;
1270 return;
1154 }
1271 }
1155 if (index > 0) {
1272 if (index > 0) {
1156 var upper_cell = this.get_cell(index-1);
1273 var upper_cell = this.get_cell(index-1);
1157 if (!upper_cell.is_mergeable()) {
1274 if (!upper_cell.is_mergeable()) {
1158 return;
1275 return;
1159 }
1276 }
1160 var upper_text = upper_cell.get_text();
1277 var upper_text = upper_cell.get_text();
1161 var text = cell.get_text();
1278 var text = cell.get_text();
1162 if (cell instanceof IPython.CodeCell) {
1279 if (cell instanceof CodeCell) {
1163 cell.set_text(upper_text+'\n'+text);
1280 cell.set_text(upper_text+'\n'+text);
1164 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1281 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1165 cell.unrender(); // Must unrender before we set_text.
1282 cell.unrender(); // Must unrender before we set_text.
1166 cell.set_text(upper_text+'\n\n'+text);
1283 cell.set_text(upper_text+'\n\n'+text);
1167 if (render) {
1284 if (render) {
1168 // The rendered state of the final cell should match
1285 // The rendered state of the final cell should match
1169 // that of the original selected cell;
1286 // that of the original selected cell;
1170 cell.render();
1287 cell.render();
1171 }
1288 }
1172 }
1289 }
1173 this.delete_cell(index-1);
1290 this.delete_cell(index-1);
1174 this.select(this.find_cell_index(cell));
1291 this.select(this.find_cell_index(cell));
1175 }
1292 }
1176 };
1293 };
1177
1294
1178 /**
1295 /**
1179 * Combine the selected cell into the cell below it.
1296 * Combine the selected cell into the cell below it.
1180 *
1297 *
1181 * @method merge_cell_below
1298 * @method merge_cell_below
1182 */
1299 */
1183 Notebook.prototype.merge_cell_below = function () {
1300 Notebook.prototype.merge_cell_below = function () {
1184 var mdc = IPython.MarkdownCell;
1301 var mdc = Cells.MarkdownCell;
1185 var rc = IPython.RawCell;
1302 var rc = Cells.RawCell;
1186 var index = this.get_selected_index();
1303 var index = this.get_selected_index();
1187 var cell = this.get_cell(index);
1304 var cell = this.get_cell(index);
1188 var render = cell.rendered;
1305 var render = cell.rendered;
1189 if (!cell.is_mergeable()) {
1306 if (!cell.is_mergeable()) {
1190 return;
1307 return;
1191 }
1308 }
1192 if (index < this.ncells()-1) {
1309 if (index < this.ncells()-1) {
1193 var lower_cell = this.get_cell(index+1);
1310 var lower_cell = this.get_cell(index+1);
1194 if (!lower_cell.is_mergeable()) {
1311 if (!lower_cell.is_mergeable()) {
1195 return;
1312 return;
1196 }
1313 }
1197 var lower_text = lower_cell.get_text();
1314 var lower_text = lower_cell.get_text();
1198 var text = cell.get_text();
1315 var text = cell.get_text();
1199 if (cell instanceof IPython.CodeCell) {
1316 if (cell instanceof CodeCell) {
1200 cell.set_text(text+'\n'+lower_text);
1317 cell.set_text(text+'\n'+lower_text);
1201 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1318 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1202 cell.unrender(); // Must unrender before we set_text.
1319 cell.unrender(); // Must unrender before we set_text.
1203 cell.set_text(text+'\n\n'+lower_text);
1320 cell.set_text(text+'\n\n'+lower_text);
1204 if (render) {
1321 if (render) {
1205 // The rendered state of the final cell should match
1322 // The rendered state of the final cell should match
1206 // that of the original selected cell;
1323 // that of the original selected cell;
1207 cell.render();
1324 cell.render();
1208 }
1325 }
1209 }
1326 }
1210 this.delete_cell(index+1);
1327 this.delete_cell(index+1);
1211 this.select(this.find_cell_index(cell));
1328 this.select(this.find_cell_index(cell));
1212 }
1329 }
1213 };
1330 };
1214
1331
1215
1332
1216 // Cell collapsing and output clearing
1333 // Cell collapsing and output clearing
1217
1334
1218 /**
1335 /**
1219 * Hide a cell's output.
1336 * Hide a cell's output.
1220 *
1337 *
1221 * @method collapse_output
1338 * @method collapse_output
1222 * @param {Number} index A cell's numeric index
1339 * @param {Number} index A cell's numeric index
1223 */
1340 */
1224 Notebook.prototype.collapse_output = function (index) {
1341 Notebook.prototype.collapse_output = function (index) {
1225 var i = this.index_or_selected(index);
1342 var i = this.index_or_selected(index);
1226 var cell = this.get_cell(i);
1343 var cell = this.get_cell(i);
1227 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1344 if (cell !== null && (cell instanceof CodeCell)) {
1228 cell.collapse_output();
1345 cell.collapse_output();
1229 this.set_dirty(true);
1346 this.set_dirty(true);
1230 }
1347 }
1231 };
1348 };
1232
1349
1233 /**
1350 /**
1234 * Hide each code cell's output area.
1351 * Hide each code cell's output area.
1235 *
1352 *
1236 * @method collapse_all_output
1353 * @method collapse_all_output
1237 */
1354 */
1238 Notebook.prototype.collapse_all_output = function () {
1355 Notebook.prototype.collapse_all_output = function () {
1239 $.map(this.get_cells(), function (cell, i) {
1356 $.map(this.get_cells(), function (cell, i) {
1240 if (cell instanceof IPython.CodeCell) {
1357 if (cell instanceof CodeCell) {
1241 cell.collapse_output();
1358 cell.collapse_output();
1242 }
1359 }
1243 });
1360 });
1244 // this should not be set if the `collapse` key is removed from nbformat
1361 // this should not be set if the `collapse` key is removed from nbformat
1245 this.set_dirty(true);
1362 this.set_dirty(true);
1246 };
1363 };
1247
1364
1248 /**
1365 /**
1249 * Show a cell's output.
1366 * Show a cell's output.
1250 *
1367 *
1251 * @method expand_output
1368 * @method expand_output
1252 * @param {Number} index A cell's numeric index
1369 * @param {Number} index A cell's numeric index
1253 */
1370 */
1254 Notebook.prototype.expand_output = function (index) {
1371 Notebook.prototype.expand_output = function (index) {
1255 var i = this.index_or_selected(index);
1372 var i = this.index_or_selected(index);
1256 var cell = this.get_cell(i);
1373 var cell = this.get_cell(i);
1257 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1374 if (cell !== null && (cell instanceof CodeCell)) {
1258 cell.expand_output();
1375 cell.expand_output();
1259 this.set_dirty(true);
1376 this.set_dirty(true);
1260 }
1377 }
1261 };
1378 };
1262
1379
1263 /**
1380 /**
1264 * Expand each code cell's output area, and remove scrollbars.
1381 * Expand each code cell's output area, and remove scrollbars.
1265 *
1382 *
1266 * @method expand_all_output
1383 * @method expand_all_output
1267 */
1384 */
1268 Notebook.prototype.expand_all_output = function () {
1385 Notebook.prototype.expand_all_output = function () {
1269 $.map(this.get_cells(), function (cell, i) {
1386 $.map(this.get_cells(), function (cell, i) {
1270 if (cell instanceof IPython.CodeCell) {
1387 if (cell instanceof CodeCell) {
1271 cell.expand_output();
1388 cell.expand_output();
1272 }
1389 }
1273 });
1390 });
1274 // this should not be set if the `collapse` key is removed from nbformat
1391 // this should not be set if the `collapse` key is removed from nbformat
1275 this.set_dirty(true);
1392 this.set_dirty(true);
1276 };
1393 };
1277
1394
1278 /**
1395 /**
1279 * Clear the selected CodeCell's output area.
1396 * Clear the selected CodeCell's output area.
1280 *
1397 *
1281 * @method clear_output
1398 * @method clear_output
1282 * @param {Number} index A cell's numeric index
1399 * @param {Number} index A cell's numeric index
1283 */
1400 */
1284 Notebook.prototype.clear_output = function (index) {
1401 Notebook.prototype.clear_output = function (index) {
1285 var i = this.index_or_selected(index);
1402 var i = this.index_or_selected(index);
1286 var cell = this.get_cell(i);
1403 var cell = this.get_cell(i);
1287 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1404 if (cell !== null && (cell instanceof CodeCell)) {
1288 cell.clear_output();
1405 cell.clear_output();
1289 this.set_dirty(true);
1406 this.set_dirty(true);
1290 }
1407 }
1291 };
1408 };
1292
1409
1293 /**
1410 /**
1294 * Clear each code cell's output area.
1411 * Clear each code cell's output area.
1295 *
1412 *
1296 * @method clear_all_output
1413 * @method clear_all_output
1297 */
1414 */
1298 Notebook.prototype.clear_all_output = function () {
1415 Notebook.prototype.clear_all_output = function () {
1299 $.map(this.get_cells(), function (cell, i) {
1416 $.map(this.get_cells(), function (cell, i) {
1300 if (cell instanceof IPython.CodeCell) {
1417 if (cell instanceof CodeCell) {
1301 cell.clear_output();
1418 cell.clear_output();
1302 }
1419 }
1303 });
1420 });
1304 this.set_dirty(true);
1421 this.set_dirty(true);
1305 };
1422 };
1306
1423
1307 /**
1424 /**
1308 * Scroll the selected CodeCell's output area.
1425 * Scroll the selected CodeCell's output area.
1309 *
1426 *
1310 * @method scroll_output
1427 * @method scroll_output
1311 * @param {Number} index A cell's numeric index
1428 * @param {Number} index A cell's numeric index
1312 */
1429 */
1313 Notebook.prototype.scroll_output = function (index) {
1430 Notebook.prototype.scroll_output = function (index) {
1314 var i = this.index_or_selected(index);
1431 var i = this.index_or_selected(index);
1315 var cell = this.get_cell(i);
1432 var cell = this.get_cell(i);
1316 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1433 if (cell !== null && (cell instanceof CodeCell)) {
1317 cell.scroll_output();
1434 cell.scroll_output();
1318 this.set_dirty(true);
1435 this.set_dirty(true);
1319 }
1436 }
1320 };
1437 };
1321
1438
1322 /**
1439 /**
1323 * Expand each code cell's output area, and add a scrollbar for long output.
1440 * Expand each code cell's output area, and add a scrollbar for long output.
1324 *
1441 *
1325 * @method scroll_all_output
1442 * @method scroll_all_output
1326 */
1443 */
1327 Notebook.prototype.scroll_all_output = function () {
1444 Notebook.prototype.scroll_all_output = function () {
1328 $.map(this.get_cells(), function (cell, i) {
1445 $.map(this.get_cells(), function (cell, i) {
1329 if (cell instanceof IPython.CodeCell) {
1446 if (cell instanceof CodeCell) {
1330 cell.scroll_output();
1447 cell.scroll_output();
1331 }
1448 }
1332 });
1449 });
1333 // this should not be set if the `collapse` key is removed from nbformat
1450 // this should not be set if the `collapse` key is removed from nbformat
1334 this.set_dirty(true);
1451 this.set_dirty(true);
1335 };
1452 };
1336
1453
1337 /** Toggle whether a cell's output is collapsed or expanded.
1454 /** Toggle whether a cell's output is collapsed or expanded.
1338 *
1455 *
1339 * @method toggle_output
1456 * @method toggle_output
1340 * @param {Number} index A cell's numeric index
1457 * @param {Number} index A cell's numeric index
1341 */
1458 */
1342 Notebook.prototype.toggle_output = function (index) {
1459 Notebook.prototype.toggle_output = function (index) {
1343 var i = this.index_or_selected(index);
1460 var i = this.index_or_selected(index);
1344 var cell = this.get_cell(i);
1461 var cell = this.get_cell(i);
1345 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1462 if (cell !== null && (cell instanceof CodeCell)) {
1346 cell.toggle_output();
1463 cell.toggle_output();
1347 this.set_dirty(true);
1464 this.set_dirty(true);
1348 }
1465 }
1349 };
1466 };
1350
1467
1351 /**
1468 /**
1352 * Hide/show the output of all cells.
1469 * Hide/show the output of all cells.
1353 *
1470 *
1354 * @method toggle_all_output
1471 * @method toggle_all_output
1355 */
1472 */
1356 Notebook.prototype.toggle_all_output = function () {
1473 Notebook.prototype.toggle_all_output = function () {
1357 $.map(this.get_cells(), function (cell, i) {
1474 $.map(this.get_cells(), function (cell, i) {
1358 if (cell instanceof IPython.CodeCell) {
1475 if (cell instanceof CodeCell) {
1359 cell.toggle_output();
1476 cell.toggle_output();
1360 }
1477 }
1361 });
1478 });
1362 // this should not be set if the `collapse` key is removed from nbformat
1479 // this should not be set if the `collapse` key is removed from nbformat
1363 this.set_dirty(true);
1480 this.set_dirty(true);
1364 };
1481 };
1365
1482
1366 /**
1483 /**
1367 * Toggle a scrollbar for long cell outputs.
1484 * Toggle a scrollbar for long cell outputs.
1368 *
1485 *
1369 * @method toggle_output_scroll
1486 * @method toggle_output_scroll
1370 * @param {Number} index A cell's numeric index
1487 * @param {Number} index A cell's numeric index
1371 */
1488 */
1372 Notebook.prototype.toggle_output_scroll = function (index) {
1489 Notebook.prototype.toggle_output_scroll = function (index) {
1373 var i = this.index_or_selected(index);
1490 var i = this.index_or_selected(index);
1374 var cell = this.get_cell(i);
1491 var cell = this.get_cell(i);
1375 if (cell !== null && (cell instanceof IPython.CodeCell)) {
1492 if (cell !== null && (cell instanceof CodeCell)) {
1376 cell.toggle_output_scroll();
1493 cell.toggle_output_scroll();
1377 this.set_dirty(true);
1494 this.set_dirty(true);
1378 }
1495 }
1379 };
1496 };
1380
1497
1381 /**
1498 /**
1382 * Toggle the scrolling of long output on all cells.
1499 * Toggle the scrolling of long output on all cells.
1383 *
1500 *
1384 * @method toggle_all_output_scrolling
1501 * @method toggle_all_output_scrolling
1385 */
1502 */
1386 Notebook.prototype.toggle_all_output_scroll = function () {
1503 Notebook.prototype.toggle_all_output_scroll = function () {
1387 $.map(this.get_cells(), function (cell, i) {
1504 $.map(this.get_cells(), function (cell, i) {
1388 if (cell instanceof IPython.CodeCell) {
1505 if (cell instanceof CodeCell) {
1389 cell.toggle_output_scroll();
1506 cell.toggle_output_scroll();
1390 }
1507 }
1391 });
1508 });
1392 // this should not be set if the `collapse` key is removed from nbformat
1509 // this should not be set if the `collapse` key is removed from nbformat
1393 this.set_dirty(true);
1510 this.set_dirty(true);
1394 };
1511 };
1395
1512
1396 // Other cell functions: line numbers, ...
1513 // Other cell functions: line numbers, ...
1397
1514
1398 /**
1515 /**
1399 * Toggle line numbers in the selected cell's input area.
1516 * Toggle line numbers in the selected cell's input area.
1400 *
1517 *
1401 * @method cell_toggle_line_numbers
1518 * @method cell_toggle_line_numbers
1402 */
1519 */
1403 Notebook.prototype.cell_toggle_line_numbers = function() {
1520 Notebook.prototype.cell_toggle_line_numbers = function() {
1404 this.get_selected_cell().toggle_line_numbers();
1521 this.get_selected_cell().toggle_line_numbers();
1405 };
1522 };
1406
1523
1407 // Session related things
1524 // Session related things
1408
1525
1409 /**
1526 /**
1410 * Start a new session and set it on each code cell.
1527 * Start a new session and set it on each code cell.
1411 *
1528 *
1412 * @method start_session
1529 * @method start_session
1413 */
1530 */
1414 Notebook.prototype.start_session = function () {
1531 Notebook.prototype.start_session = function () {
1415 this.session = new IPython.Session(this, this.options);
1532 this.session = new Session(this, this.options);
1416 this.session.start($.proxy(this._session_started, this));
1533 this.session.start($.proxy(this._session_started, this));
1417 };
1534 };
1418
1535
1419
1536
1420 /**
1537 /**
1421 * Once a session is started, link the code cells to the kernel and pass the
1538 * Once a session is started, link the code cells to the kernel and pass the
1422 * comm manager to the widget manager
1539 * comm manager to the widget manager
1423 *
1540 *
1424 */
1541 */
1425 Notebook.prototype._session_started = function(){
1542 Notebook.prototype._session_started = function(){
1426 this.kernel = this.session.kernel;
1543 this.kernel = this.session.kernel;
1427 var ncells = this.ncells();
1544 var ncells = this.ncells();
1428 for (var i=0; i<ncells; i++) {
1545 for (var i=0; i<ncells; i++) {
1429 var cell = this.get_cell(i);
1546 var cell = this.get_cell(i);
1430 if (cell instanceof IPython.CodeCell) {
1547 if (cell instanceof CodeCell) {
1431 cell.set_kernel(this.session.kernel);
1548 cell.set_kernel(this.session.kernel);
1432 }
1549 }
1433 }
1550 }
1434 };
1551 };
1435
1552
1436 /**
1553 /**
1437 * Prompt the user to restart the IPython kernel.
1554 * Prompt the user to restart the IPython kernel.
1438 *
1555 *
1439 * @method restart_kernel
1556 * @method restart_kernel
1440 */
1557 */
1441 Notebook.prototype.restart_kernel = function () {
1558 Notebook.prototype.restart_kernel = function () {
1442 var that = this;
1559 var that = this;
1443 IPython.dialog.modal({
1560 Dialog.modal({
1444 title : "Restart kernel or continue running?",
1561 title : "Restart kernel or continue running?",
1445 body : $("<p/>").text(
1562 body : $("<p/>").text(
1446 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1563 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1447 ),
1564 ),
1448 buttons : {
1565 buttons : {
1449 "Continue running" : {},
1566 "Continue running" : {},
1450 "Restart" : {
1567 "Restart" : {
1451 "class" : "btn-danger",
1568 "class" : "btn-danger",
1452 "click" : function() {
1569 "click" : function() {
1453 that.session.restart_kernel();
1570 that.session.restart_kernel();
1454 }
1571 }
1455 }
1572 }
1456 }
1573 }
1457 });
1574 });
1458 };
1575 };
1459
1576
1460 /**
1577 /**
1461 * Execute or render cell outputs and go into command mode.
1578 * Execute or render cell outputs and go into command mode.
1462 *
1579 *
1463 * @method execute_cell
1580 * @method execute_cell
1464 */
1581 */
1465 Notebook.prototype.execute_cell = function () {
1582 Notebook.prototype.execute_cell = function () {
1466 // mode = shift, ctrl, alt
1583 // mode = shift, ctrl, alt
1467 var cell = this.get_selected_cell();
1584 var cell = this.get_selected_cell();
1468 var cell_index = this.find_cell_index(cell);
1585 var cell_index = this.find_cell_index(cell);
1469
1586
1470 cell.execute();
1587 cell.execute();
1471 this.command_mode();
1588 this.command_mode();
1472 this.set_dirty(true);
1589 this.set_dirty(true);
1473 };
1590 };
1474
1591
1475 /**
1592 /**
1476 * Execute or render cell outputs and insert a new cell below.
1593 * Execute or render cell outputs and insert a new cell below.
1477 *
1594 *
1478 * @method execute_cell_and_insert_below
1595 * @method execute_cell_and_insert_below
1479 */
1596 */
1480 Notebook.prototype.execute_cell_and_insert_below = function () {
1597 Notebook.prototype.execute_cell_and_insert_below = function () {
1481 var cell = this.get_selected_cell();
1598 var cell = this.get_selected_cell();
1482 var cell_index = this.find_cell_index(cell);
1599 var cell_index = this.find_cell_index(cell);
1483
1600
1484 cell.execute();
1601 cell.execute();
1485
1602
1486 // If we are at the end always insert a new cell and return
1603 // If we are at the end always insert a new cell and return
1487 if (cell_index === (this.ncells()-1)) {
1604 if (cell_index === (this.ncells()-1)) {
1488 this.command_mode();
1605 this.command_mode();
1489 this.insert_cell_below();
1606 this.insert_cell_below();
1490 this.select(cell_index+1);
1607 this.select(cell_index+1);
1491 this.edit_mode();
1608 this.edit_mode();
1492 this.scroll_to_bottom();
1609 this.scroll_to_bottom();
1493 this.set_dirty(true);
1610 this.set_dirty(true);
1494 return;
1611 return;
1495 }
1612 }
1496
1613
1497 this.command_mode();
1614 this.command_mode();
1498 this.insert_cell_below();
1615 this.insert_cell_below();
1499 this.select(cell_index+1);
1616 this.select(cell_index+1);
1500 this.edit_mode();
1617 this.edit_mode();
1501 this.set_dirty(true);
1618 this.set_dirty(true);
1502 };
1619 };
1503
1620
1504 /**
1621 /**
1505 * Execute or render cell outputs and select the next cell.
1622 * Execute or render cell outputs and select the next cell.
1506 *
1623 *
1507 * @method execute_cell_and_select_below
1624 * @method execute_cell_and_select_below
1508 */
1625 */
1509 Notebook.prototype.execute_cell_and_select_below = function () {
1626 Notebook.prototype.execute_cell_and_select_below = function () {
1510
1627
1511 var cell = this.get_selected_cell();
1628 var cell = this.get_selected_cell();
1512 var cell_index = this.find_cell_index(cell);
1629 var cell_index = this.find_cell_index(cell);
1513
1630
1514 cell.execute();
1631 cell.execute();
1515
1632
1516 // If we are at the end always insert a new cell and return
1633 // If we are at the end always insert a new cell and return
1517 if (cell_index === (this.ncells()-1)) {
1634 if (cell_index === (this.ncells()-1)) {
1518 this.command_mode();
1635 this.command_mode();
1519 this.insert_cell_below();
1636 this.insert_cell_below();
1520 this.select(cell_index+1);
1637 this.select(cell_index+1);
1521 this.edit_mode();
1638 this.edit_mode();
1522 this.scroll_to_bottom();
1639 this.scroll_to_bottom();
1523 this.set_dirty(true);
1640 this.set_dirty(true);
1524 return;
1641 return;
1525 }
1642 }
1526
1643
1527 this.command_mode();
1644 this.command_mode();
1528 this.select(cell_index+1);
1645 this.select(cell_index+1);
1529 this.focus_cell();
1646 this.focus_cell();
1530 this.set_dirty(true);
1647 this.set_dirty(true);
1531 };
1648 };
1532
1649
1533 /**
1650 /**
1534 * Execute all cells below the selected cell.
1651 * Execute all cells below the selected cell.
1535 *
1652 *
1536 * @method execute_cells_below
1653 * @method execute_cells_below
1537 */
1654 */
1538 Notebook.prototype.execute_cells_below = function () {
1655 Notebook.prototype.execute_cells_below = function () {
1539 this.execute_cell_range(this.get_selected_index(), this.ncells());
1656 this.execute_cell_range(this.get_selected_index(), this.ncells());
1540 this.scroll_to_bottom();
1657 this.scroll_to_bottom();
1541 };
1658 };
1542
1659
1543 /**
1660 /**
1544 * Execute all cells above the selected cell.
1661 * Execute all cells above the selected cell.
1545 *
1662 *
1546 * @method execute_cells_above
1663 * @method execute_cells_above
1547 */
1664 */
1548 Notebook.prototype.execute_cells_above = function () {
1665 Notebook.prototype.execute_cells_above = function () {
1549 this.execute_cell_range(0, this.get_selected_index());
1666 this.execute_cell_range(0, this.get_selected_index());
1550 };
1667 };
1551
1668
1552 /**
1669 /**
1553 * Execute all cells.
1670 * Execute all cells.
1554 *
1671 *
1555 * @method execute_all_cells
1672 * @method execute_all_cells
1556 */
1673 */
1557 Notebook.prototype.execute_all_cells = function () {
1674 Notebook.prototype.execute_all_cells = function () {
1558 this.execute_cell_range(0, this.ncells());
1675 this.execute_cell_range(0, this.ncells());
1559 this.scroll_to_bottom();
1676 this.scroll_to_bottom();
1560 };
1677 };
1561
1678
1562 /**
1679 /**
1563 * Execute a contiguous range of cells.
1680 * Execute a contiguous range of cells.
1564 *
1681 *
1565 * @method execute_cell_range
1682 * @method execute_cell_range
1566 * @param {Number} start Index of the first cell to execute (inclusive)
1683 * @param {Number} start Index of the first cell to execute (inclusive)
1567 * @param {Number} end Index of the last cell to execute (exclusive)
1684 * @param {Number} end Index of the last cell to execute (exclusive)
1568 */
1685 */
1569 Notebook.prototype.execute_cell_range = function (start, end) {
1686 Notebook.prototype.execute_cell_range = function (start, end) {
1570 this.command_mode();
1687 this.command_mode();
1571 for (var i=start; i<end; i++) {
1688 for (var i=start; i<end; i++) {
1572 this.select(i);
1689 this.select(i);
1573 this.execute_cell();
1690 this.execute_cell();
1574 }
1691 }
1575 };
1692 };
1576
1693
1577 // Persistance and loading
1694 // Persistance and loading
1578
1695
1579 /**
1696 /**
1580 * Getter method for this notebook's name.
1697 * Getter method for this notebook's name.
1581 *
1698 *
1582 * @method get_notebook_name
1699 * @method get_notebook_name
1583 * @return {String} This notebook's name (excluding file extension)
1700 * @return {String} This notebook's name (excluding file extension)
1584 */
1701 */
1585 Notebook.prototype.get_notebook_name = function () {
1702 Notebook.prototype.get_notebook_name = function () {
1586 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1703 var nbname = this.notebook_name.substring(0,this.notebook_name.length-6);
1587 return nbname;
1704 return nbname;
1588 };
1705 };
1589
1706
1590 /**
1707 /**
1591 * Setter method for this notebook's name.
1708 * Setter method for this notebook's name.
1592 *
1709 *
1593 * @method set_notebook_name
1710 * @method set_notebook_name
1594 * @param {String} name A new name for this notebook
1711 * @param {String} name A new name for this notebook
1595 */
1712 */
1596 Notebook.prototype.set_notebook_name = function (name) {
1713 Notebook.prototype.set_notebook_name = function (name) {
1597 this.notebook_name = name;
1714 this.notebook_name = name;
1598 };
1715 };
1599
1716
1600 /**
1717 /**
1601 * Check that a notebook's name is valid.
1718 * Check that a notebook's name is valid.
1602 *
1719 *
1603 * @method test_notebook_name
1720 * @method test_notebook_name
1604 * @param {String} nbname A name for this notebook
1721 * @param {String} nbname A name for this notebook
1605 * @return {Boolean} True if the name is valid, false if invalid
1722 * @return {Boolean} True if the name is valid, false if invalid
1606 */
1723 */
1607 Notebook.prototype.test_notebook_name = function (nbname) {
1724 Notebook.prototype.test_notebook_name = function (nbname) {
1608 nbname = nbname || '';
1725 nbname = nbname || '';
1609 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1726 if (nbname.length>0 && !this.notebook_name_blacklist_re.test(nbname)) {
1610 return true;
1727 return true;
1611 } else {
1728 } else {
1612 return false;
1729 return false;
1613 }
1730 }
1614 };
1731 };
1615
1732
1616 /**
1733 /**
1617 * Load a notebook from JSON (.ipynb).
1734 * Load a notebook from JSON (.ipynb).
1618 *
1735 *
1619 * This currently handles one worksheet: others are deleted.
1736 * This currently handles one worksheet: others are deleted.
1620 *
1737 *
1621 * @method fromJSON
1738 * @method fromJSON
1622 * @param {Object} data JSON representation of a notebook
1739 * @param {Object} data JSON representation of a notebook
1623 */
1740 */
1624 Notebook.prototype.fromJSON = function (data) {
1741 Notebook.prototype.fromJSON = function (data) {
1625 var content = data.content;
1742 var content = data.content;
1626 var ncells = this.ncells();
1743 var ncells = this.ncells();
1627 var i;
1744 var i;
1628 for (i=0; i<ncells; i++) {
1745 for (i=0; i<ncells; i++) {
1629 // Always delete cell 0 as they get renumbered as they are deleted.
1746 // Always delete cell 0 as they get renumbered as they are deleted.
1630 this.delete_cell(0);
1747 this.delete_cell(0);
1631 }
1748 }
1632 // Save the metadata and name.
1749 // Save the metadata and name.
1633 this.metadata = content.metadata;
1750 this.metadata = content.metadata;
1634 this.notebook_name = data.name;
1751 this.notebook_name = data.name;
1635 var trusted = true;
1752 var trusted = true;
1636 // Only handle 1 worksheet for now.
1753 // Only handle 1 worksheet for now.
1637 var worksheet = content.worksheets[0];
1754 var worksheet = content.worksheets[0];
1638 if (worksheet !== undefined) {
1755 if (worksheet !== undefined) {
1639 if (worksheet.metadata) {
1756 if (worksheet.metadata) {
1640 this.worksheet_metadata = worksheet.metadata;
1757 this.worksheet_metadata = worksheet.metadata;
1641 }
1758 }
1642 var new_cells = worksheet.cells;
1759 var new_cells = worksheet.cells;
1643 ncells = new_cells.length;
1760 ncells = new_cells.length;
1644 var cell_data = null;
1761 var cell_data = null;
1645 var new_cell = null;
1762 var new_cell = null;
1646 for (i=0; i<ncells; i++) {
1763 for (i=0; i<ncells; i++) {
1647 cell_data = new_cells[i];
1764 cell_data = new_cells[i];
1648 // VERSIONHACK: plaintext -> raw
1765 // VERSIONHACK: plaintext -> raw
1649 // handle never-released plaintext name for raw cells
1766 // handle never-released plaintext name for raw cells
1650 if (cell_data.cell_type === 'plaintext'){
1767 if (cell_data.cell_type === 'plaintext'){
1651 cell_data.cell_type = 'raw';
1768 cell_data.cell_type = 'raw';
1652 }
1769 }
1653
1770
1654 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1771 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1655 new_cell.fromJSON(cell_data);
1772 new_cell.fromJSON(cell_data);
1656 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1773 if (new_cell.cell_type == 'code' && !new_cell.output_area.trusted) {
1657 trusted = false;
1774 trusted = false;
1658 }
1775 }
1659 }
1776 }
1660 }
1777 }
1661 if (trusted != this.trusted) {
1778 if (trusted != this.trusted) {
1662 this.trusted = trusted;
1779 this.trusted = trusted;
1663 $([IPython.events]).trigger("trust_changed.Notebook", trusted);
1780 $([Events]).trigger("trust_changed.Notebook", trusted);
1664 }
1781 }
1665 if (content.worksheets.length > 1) {
1782 if (content.worksheets.length > 1) {
1666 IPython.dialog.modal({
1783 Dialog.modal({
1667 title : "Multiple worksheets",
1784 title : "Multiple worksheets",
1668 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1785 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1669 "but this version of IPython can only handle the first. " +
1786 "but this version of IPython can only handle the first. " +
1670 "If you save this notebook, worksheets after the first will be lost.",
1787 "If you save this notebook, worksheets after the first will be lost.",
1671 buttons : {
1788 buttons : {
1672 OK : {
1789 OK : {
1673 class : "btn-danger"
1790 class : "btn-danger"
1674 }
1791 }
1675 }
1792 }
1676 });
1793 });
1677 }
1794 }
1678 };
1795 };
1679
1796
1680 /**
1797 /**
1681 * Dump this notebook into a JSON-friendly object.
1798 * Dump this notebook into a JSON-friendly object.
1682 *
1799 *
1683 * @method toJSON
1800 * @method toJSON
1684 * @return {Object} A JSON-friendly representation of this notebook.
1801 * @return {Object} A JSON-friendly representation of this notebook.
1685 */
1802 */
1686 Notebook.prototype.toJSON = function () {
1803 Notebook.prototype.toJSON = function () {
1687 var cells = this.get_cells();
1804 var cells = this.get_cells();
1688 var ncells = cells.length;
1805 var ncells = cells.length;
1689 var cell_array = new Array(ncells);
1806 var cell_array = new Array(ncells);
1690 var trusted = true;
1807 var trusted = true;
1691 for (var i=0; i<ncells; i++) {
1808 for (var i=0; i<ncells; i++) {
1692 var cell = cells[i];
1809 var cell = cells[i];
1693 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1810 if (cell.cell_type == 'code' && !cell.output_area.trusted) {
1694 trusted = false;
1811 trusted = false;
1695 }
1812 }
1696 cell_array[i] = cell.toJSON();
1813 cell_array[i] = cell.toJSON();
1697 }
1814 }
1698 var data = {
1815 var data = {
1699 // Only handle 1 worksheet for now.
1816 // Only handle 1 worksheet for now.
1700 worksheets : [{
1817 worksheets : [{
1701 cells: cell_array,
1818 cells: cell_array,
1702 metadata: this.worksheet_metadata
1819 metadata: this.worksheet_metadata
1703 }],
1820 }],
1704 metadata : this.metadata
1821 metadata : this.metadata
1705 };
1822 };
1706 if (trusted != this.trusted) {
1823 if (trusted != this.trusted) {
1707 this.trusted = trusted;
1824 this.trusted = trusted;
1708 $([IPython.events]).trigger("trust_changed.Notebook", trusted);
1825 $([Events]).trigger("trust_changed.Notebook", trusted);
1709 }
1826 }
1710 return data;
1827 return data;
1711 };
1828 };
1712
1829
1713 /**
1830 /**
1714 * Start an autosave timer, for periodically saving the notebook.
1831 * Start an autosave timer, for periodically saving the notebook.
1715 *
1832 *
1716 * @method set_autosave_interval
1833 * @method set_autosave_interval
1717 * @param {Integer} interval the autosave interval in milliseconds
1834 * @param {Integer} interval the autosave interval in milliseconds
1718 */
1835 */
1719 Notebook.prototype.set_autosave_interval = function (interval) {
1836 Notebook.prototype.set_autosave_interval = function (interval) {
1720 var that = this;
1837 var that = this;
1721 // clear previous interval, so we don't get simultaneous timers
1838 // clear previous interval, so we don't get simultaneous timers
1722 if (this.autosave_timer) {
1839 if (this.autosave_timer) {
1723 clearInterval(this.autosave_timer);
1840 clearInterval(this.autosave_timer);
1724 }
1841 }
1725
1842
1726 this.autosave_interval = this.minimum_autosave_interval = interval;
1843 this.autosave_interval = this.minimum_autosave_interval = interval;
1727 if (interval) {
1844 if (interval) {
1728 this.autosave_timer = setInterval(function() {
1845 this.autosave_timer = setInterval(function() {
1729 if (that.dirty) {
1846 if (that.dirty) {
1730 that.save_notebook();
1847 that.save_notebook();
1731 }
1848 }
1732 }, interval);
1849 }, interval);
1733 $([IPython.events]).trigger("autosave_enabled.Notebook", interval);
1850 $([Events]).trigger("autosave_enabled.Notebook", interval);
1734 } else {
1851 } else {
1735 this.autosave_timer = null;
1852 this.autosave_timer = null;
1736 $([IPython.events]).trigger("autosave_disabled.Notebook");
1853 $([Events]).trigger("autosave_disabled.Notebook");
1737 }
1854 }
1738 };
1855 };
1739
1856
1740 /**
1857 /**
1741 * Save this notebook on the server. This becomes a notebook instance's
1858 * Save this notebook on the server. This becomes a notebook instance's
1742 * .save_notebook method *after* the entire notebook has been loaded.
1859 * .save_notebook method *after* the entire notebook has been loaded.
1743 *
1860 *
1744 * @method save_notebook
1861 * @method save_notebook
1745 */
1862 */
1746 Notebook.prototype.save_notebook = function (extra_settings) {
1863 Notebook.prototype.save_notebook = function (extra_settings) {
1747 // Create a JSON model to be sent to the server.
1864 // Create a JSON model to be sent to the server.
1748 var model = {};
1865 var model = {};
1749 model.name = this.notebook_name;
1866 model.name = this.notebook_name;
1750 model.path = this.notebook_path;
1867 model.path = this.notebook_path;
1751 model.content = this.toJSON();
1868 model.content = this.toJSON();
1752 model.content.nbformat = this.nbformat;
1869 model.content.nbformat = this.nbformat;
1753 model.content.nbformat_minor = this.nbformat_minor;
1870 model.content.nbformat_minor = this.nbformat_minor;
1754 // time the ajax call for autosave tuning purposes.
1871 // time the ajax call for autosave tuning purposes.
1755 var start = new Date().getTime();
1872 var start = new Date().getTime();
1756 // We do the call with settings so we can set cache to false.
1873 // We do the call with settings so we can set cache to false.
1757 var settings = {
1874 var settings = {
1758 processData : false,
1875 processData : false,
1759 cache : false,
1876 cache : false,
1760 type : "PUT",
1877 type : "PUT",
1761 data : JSON.stringify(model),
1878 data : JSON.stringify(model),
1762 headers : {'Content-Type': 'application/json'},
1879 headers : {'Content-Type': 'application/json'},
1763 success : $.proxy(this.save_notebook_success, this, start),
1880 success : $.proxy(this.save_notebook_success, this, start),
1764 error : $.proxy(this.save_notebook_error, this)
1881 error : $.proxy(this.save_notebook_error, this)
1765 };
1882 };
1766 if (extra_settings) {
1883 if (extra_settings) {
1767 for (var key in extra_settings) {
1884 for (var key in extra_settings) {
1768 settings[key] = extra_settings[key];
1885 settings[key] = extra_settings[key];
1769 }
1886 }
1770 }
1887 }
1771 $([IPython.events]).trigger('notebook_saving.Notebook');
1888 $([Events]).trigger('notebook_saving.Notebook');
1772 var url = utils.url_join_encode(
1889 var url = utils.url_join_encode(
1773 this.base_url,
1890 this.base_url,
1774 'api/notebooks',
1891 'api/notebooks',
1775 this.notebook_path,
1892 this.notebook_path,
1776 this.notebook_name
1893 this.notebook_name
1777 );
1894 );
1778 $.ajax(url, settings);
1895 $.ajax(url, settings);
1779 };
1896 };
1780
1897
1781 /**
1898 /**
1782 * Success callback for saving a notebook.
1899 * Success callback for saving a notebook.
1783 *
1900 *
1784 * @method save_notebook_success
1901 * @method save_notebook_success
1785 * @param {Integer} start the time when the save request started
1902 * @param {Integer} start the time when the save request started
1786 * @param {Object} data JSON representation of a notebook
1903 * @param {Object} data JSON representation of a notebook
1787 * @param {String} status Description of response status
1904 * @param {String} status Description of response status
1788 * @param {jqXHR} xhr jQuery Ajax object
1905 * @param {jqXHR} xhr jQuery Ajax object
1789 */
1906 */
1790 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1907 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1791 this.set_dirty(false);
1908 this.set_dirty(false);
1792 $([IPython.events]).trigger('notebook_saved.Notebook');
1909 $([Events]).trigger('notebook_saved.Notebook');
1793 this._update_autosave_interval(start);
1910 this._update_autosave_interval(start);
1794 if (this._checkpoint_after_save) {
1911 if (this._checkpoint_after_save) {
1795 this.create_checkpoint();
1912 this.create_checkpoint();
1796 this._checkpoint_after_save = false;
1913 this._checkpoint_after_save = false;
1797 }
1914 }
1798 };
1915 };
1799
1916
1800 /**
1917 /**
1801 * update the autosave interval based on how long the last save took
1918 * update the autosave interval based on how long the last save took
1802 *
1919 *
1803 * @method _update_autosave_interval
1920 * @method _update_autosave_interval
1804 * @param {Integer} timestamp when the save request started
1921 * @param {Integer} timestamp when the save request started
1805 */
1922 */
1806 Notebook.prototype._update_autosave_interval = function (start) {
1923 Notebook.prototype._update_autosave_interval = function (start) {
1807 var duration = (new Date().getTime() - start);
1924 var duration = (new Date().getTime() - start);
1808 if (this.autosave_interval) {
1925 if (this.autosave_interval) {
1809 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1926 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1810 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1927 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1811 // round to 10 seconds, otherwise we will be setting a new interval too often
1928 // round to 10 seconds, otherwise we will be setting a new interval too often
1812 interval = 10000 * Math.round(interval / 10000);
1929 interval = 10000 * Math.round(interval / 10000);
1813 // set new interval, if it's changed
1930 // set new interval, if it's changed
1814 if (interval != this.autosave_interval) {
1931 if (interval != this.autosave_interval) {
1815 this.set_autosave_interval(interval);
1932 this.set_autosave_interval(interval);
1816 }
1933 }
1817 }
1934 }
1818 };
1935 };
1819
1936
1820 /**
1937 /**
1821 * Failure callback for saving a notebook.
1938 * Failure callback for saving a notebook.
1822 *
1939 *
1823 * @method save_notebook_error
1940 * @method save_notebook_error
1824 * @param {jqXHR} xhr jQuery Ajax object
1941 * @param {jqXHR} xhr jQuery Ajax object
1825 * @param {String} status Description of response status
1942 * @param {String} status Description of response status
1826 * @param {String} error HTTP error message
1943 * @param {String} error HTTP error message
1827 */
1944 */
1828 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1945 Notebook.prototype.save_notebook_error = function (xhr, status, error) {
1829 $([IPython.events]).trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1946 $([Events]).trigger('notebook_save_failed.Notebook', [xhr, status, error]);
1830 };
1947 };
1831
1948
1832 /**
1949 /**
1833 * Explicitly trust the output of this notebook.
1950 * Explicitly trust the output of this notebook.
1834 *
1951 *
1835 * @method trust_notebook
1952 * @method trust_notebook
1836 */
1953 */
1837 Notebook.prototype.trust_notebook = function (extra_settings) {
1954 Notebook.prototype.trust_notebook = function (extra_settings) {
1838 var body = $("<div>").append($("<p>")
1955 var body = $("<div>").append($("<p>")
1839 .text("A trusted IPython notebook may execute hidden malicious code ")
1956 .text("A trusted IPython notebook may execute hidden malicious code ")
1840 .append($("<strong>")
1957 .append($("<strong>")
1841 .append(
1958 .append(
1842 $("<em>").text("when you open it")
1959 $("<em>").text("when you open it")
1843 )
1960 )
1844 ).append(".").append(
1961 ).append(".").append(
1845 " Selecting trust will immediately reload this notebook in a trusted state."
1962 " Selecting trust will immediately reload this notebook in a trusted state."
1846 ).append(
1963 ).append(
1847 " For more information, see the "
1964 " For more information, see the "
1848 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
1965 ).append($("<a>").attr("href", "http://ipython.org/ipython-doc/2/notebook/security.html")
1849 .text("IPython security documentation")
1966 .text("IPython security documentation")
1850 ).append(".")
1967 ).append(".")
1851 );
1968 );
1852
1969
1853 var nb = this;
1970 var nb = this;
1854 IPython.dialog.modal({
1971 Dialog.modal({
1855 title: "Trust this notebook?",
1972 title: "Trust this notebook?",
1856 body: body,
1973 body: body,
1857
1974
1858 buttons: {
1975 buttons: {
1859 Cancel : {},
1976 Cancel : {},
1860 Trust : {
1977 Trust : {
1861 class : "btn-danger",
1978 class : "btn-danger",
1862 click : function () {
1979 click : function () {
1863 var cells = nb.get_cells();
1980 var cells = nb.get_cells();
1864 for (var i = 0; i < cells.length; i++) {
1981 for (var i = 0; i < cells.length; i++) {
1865 var cell = cells[i];
1982 var cell = cells[i];
1866 if (cell.cell_type == 'code') {
1983 if (cell.cell_type == 'code') {
1867 cell.output_area.trusted = true;
1984 cell.output_area.trusted = true;
1868 }
1985 }
1869 }
1986 }
1870 $([IPython.events]).on('notebook_saved.Notebook', function () {
1987 $([Events]).on('notebook_saved.Notebook', function () {
1871 window.location.reload();
1988 window.location.reload();
1872 });
1989 });
1873 nb.save_notebook();
1990 nb.save_notebook();
1874 }
1991 }
1875 }
1992 }
1876 }
1993 }
1877 });
1994 });
1878 };
1995 };
1879
1996
1880 Notebook.prototype.new_notebook = function(){
1997 Notebook.prototype.new_notebook = function(){
1881 var path = this.notebook_path;
1998 var path = this.notebook_path;
1882 var base_url = this.base_url;
1999 var base_url = this.base_url;
1883 var settings = {
2000 var settings = {
1884 processData : false,
2001 processData : false,
1885 cache : false,
2002 cache : false,
1886 type : "POST",
2003 type : "POST",
1887 dataType : "json",
2004 dataType : "json",
1888 async : false,
2005 async : false,
1889 success : function (data, status, xhr){
2006 success : function (data, status, xhr){
1890 var notebook_name = data.name;
2007 var notebook_name = data.name;
1891 window.open(
2008 window.open(
1892 utils.url_join_encode(
2009 utils.url_join_encode(
1893 base_url,
2010 base_url,
1894 'notebooks',
2011 'notebooks',
1895 path,
2012 path,
1896 notebook_name
2013 notebook_name
1897 ),
2014 ),
1898 '_blank'
2015 '_blank'
1899 );
2016 );
1900 },
2017 },
1901 error : utils.log_ajax_error,
2018 error : utils.log_ajax_error,
1902 };
2019 };
1903 var url = utils.url_join_encode(
2020 var url = utils.url_join_encode(
1904 base_url,
2021 base_url,
1905 'api/notebooks',
2022 'api/notebooks',
1906 path
2023 path
1907 );
2024 );
1908 $.ajax(url,settings);
2025 $.ajax(url,settings);
1909 };
2026 };
1910
2027
1911
2028
1912 Notebook.prototype.copy_notebook = function(){
2029 Notebook.prototype.copy_notebook = function(){
1913 var path = this.notebook_path;
2030 var path = this.notebook_path;
1914 var base_url = this.base_url;
2031 var base_url = this.base_url;
1915 var settings = {
2032 var settings = {
1916 processData : false,
2033 processData : false,
1917 cache : false,
2034 cache : false,
1918 type : "POST",
2035 type : "POST",
1919 dataType : "json",
2036 dataType : "json",
1920 data : JSON.stringify({copy_from : this.notebook_name}),
2037 data : JSON.stringify({copy_from : this.notebook_name}),
1921 async : false,
2038 async : false,
1922 success : function (data, status, xhr) {
2039 success : function (data, status, xhr) {
1923 window.open(utils.url_join_encode(
2040 window.open(utils.url_join_encode(
1924 base_url,
2041 base_url,
1925 'notebooks',
2042 'notebooks',
1926 data.path,
2043 data.path,
1927 data.name
2044 data.name
1928 ), '_blank');
2045 ), '_blank');
1929 },
2046 },
1930 error : utils.log_ajax_error,
2047 error : utils.log_ajax_error,
1931 };
2048 };
1932 var url = utils.url_join_encode(
2049 var url = utils.url_join_encode(
1933 base_url,
2050 base_url,
1934 'api/notebooks',
2051 'api/notebooks',
1935 path
2052 path
1936 );
2053 );
1937 $.ajax(url,settings);
2054 $.ajax(url,settings);
1938 };
2055 };
1939
2056
1940 Notebook.prototype.rename = function (nbname) {
2057 Notebook.prototype.rename = function (nbname) {
1941 var that = this;
2058 var that = this;
1942 if (!nbname.match(/\.ipynb$/)) {
2059 if (!nbname.match(/\.ipynb$/)) {
1943 nbname = nbname + ".ipynb";
2060 nbname = nbname + ".ipynb";
1944 }
2061 }
1945 var data = {name: nbname};
2062 var data = {name: nbname};
1946 var settings = {
2063 var settings = {
1947 processData : false,
2064 processData : false,
1948 cache : false,
2065 cache : false,
1949 type : "PATCH",
2066 type : "PATCH",
1950 data : JSON.stringify(data),
2067 data : JSON.stringify(data),
1951 dataType: "json",
2068 dataType: "json",
1952 headers : {'Content-Type': 'application/json'},
2069 headers : {'Content-Type': 'application/json'},
1953 success : $.proxy(that.rename_success, this),
2070 success : $.proxy(that.rename_success, this),
1954 error : $.proxy(that.rename_error, this)
2071 error : $.proxy(that.rename_error, this)
1955 };
2072 };
1956 $([IPython.events]).trigger('rename_notebook.Notebook', data);
2073 $([Events]).trigger('rename_notebook.Notebook', data);
1957 var url = utils.url_join_encode(
2074 var url = utils.url_join_encode(
1958 this.base_url,
2075 this.base_url,
1959 'api/notebooks',
2076 'api/notebooks',
1960 this.notebook_path,
2077 this.notebook_path,
1961 this.notebook_name
2078 this.notebook_name
1962 );
2079 );
1963 $.ajax(url, settings);
2080 $.ajax(url, settings);
1964 };
2081 };
1965
2082
1966 Notebook.prototype.delete = function () {
2083 Notebook.prototype.delete = function () {
1967 var that = this;
2084 var that = this;
1968 var settings = {
2085 var settings = {
1969 processData : false,
2086 processData : false,
1970 cache : false,
2087 cache : false,
1971 type : "DELETE",
2088 type : "DELETE",
1972 dataType: "json",
2089 dataType: "json",
1973 error : utils.log_ajax_error,
2090 error : utils.log_ajax_error,
1974 };
2091 };
1975 var url = utils.url_join_encode(
2092 var url = utils.url_join_encode(
1976 this.base_url,
2093 this.base_url,
1977 'api/notebooks',
2094 'api/notebooks',
1978 this.notebook_path,
2095 this.notebook_path,
1979 this.notebook_name
2096 this.notebook_name
1980 );
2097 );
1981 $.ajax(url, settings);
2098 $.ajax(url, settings);
1982 };
2099 };
1983
2100
1984
2101
1985 Notebook.prototype.rename_success = function (json, status, xhr) {
2102 Notebook.prototype.rename_success = function (json, status, xhr) {
1986 var name = this.notebook_name = json.name;
2103 var name = this.notebook_name = json.name;
1987 var path = json.path;
2104 var path = json.path;
1988 this.session.rename_notebook(name, path);
2105 this.session.rename_notebook(name, path);
1989 $([IPython.events]).trigger('notebook_renamed.Notebook', json);
2106 $([Events]).trigger('notebook_renamed.Notebook', json);
1990 };
2107 };
1991
2108
1992 Notebook.prototype.rename_error = function (xhr, status, error) {
2109 Notebook.prototype.rename_error = function (xhr, status, error) {
1993 var that = this;
2110 var that = this;
1994 var dialog = $('<div/>').append(
2111 var dialog = $('<div/>').append(
1995 $("<p/>").addClass("rename-message")
2112 $("<p/>").addClass("rename-message")
1996 .text('This notebook name already exists.')
2113 .text('This notebook name already exists.')
1997 );
2114 );
1998 $([IPython.events]).trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
2115 $([Events]).trigger('notebook_rename_failed.Notebook', [xhr, status, error]);
1999 IPython.dialog.modal({
2116 Dialog.modal({
2000 title: "Notebook Rename Error!",
2117 title: "Notebook Rename Error!",
2001 body: dialog,
2118 body: dialog,
2002 buttons : {
2119 buttons : {
2003 "Cancel": {},
2120 "Cancel": {},
2004 "OK": {
2121 "OK": {
2005 class: "btn-primary",
2122 class: "btn-primary",
2006 click: function () {
2123 click: function () {
2007 IPython.save_widget.rename_notebook();
2124 save_widget.rename_notebook();
2008 }}
2125 }}
2009 },
2126 },
2010 open : function (event, ui) {
2127 open : function (event, ui) {
2011 var that = $(this);
2128 var that = $(this);
2012 // Upon ENTER, click the OK button.
2129 // Upon ENTER, click the OK button.
2013 that.find('input[type="text"]').keydown(function (event, ui) {
2130 that.find('input[type="text"]').keydown(function (event, ui) {
2014 if (event.which === IPython.keyboard.keycodes.enter) {
2131 if (event.which === keyboard.keycodes.enter) {
2015 that.find('.btn-primary').first().click();
2132 that.find('.btn-primary').first().click();
2016 }
2133 }
2017 });
2134 });
2018 that.find('input[type="text"]').focus();
2135 that.find('input[type="text"]').focus();
2019 }
2136 }
2020 });
2137 });
2021 };
2138 };
2022
2139
2023 /**
2140 /**
2024 * Request a notebook's data from the server.
2141 * Request a notebook's data from the server.
2025 *
2142 *
2026 * @method load_notebook
2143 * @method load_notebook
2027 * @param {String} notebook_name and path A notebook to load
2144 * @param {String} notebook_name and path A notebook to load
2028 */
2145 */
2029 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
2146 Notebook.prototype.load_notebook = function (notebook_name, notebook_path) {
2030 var that = this;
2147 var that = this;
2031 this.notebook_name = notebook_name;
2148 this.notebook_name = notebook_name;
2032 this.notebook_path = notebook_path;
2149 this.notebook_path = notebook_path;
2033 // We do the call with settings so we can set cache to false.
2150 // We do the call with settings so we can set cache to false.
2034 var settings = {
2151 var settings = {
2035 processData : false,
2152 processData : false,
2036 cache : false,
2153 cache : false,
2037 type : "GET",
2154 type : "GET",
2038 dataType : "json",
2155 dataType : "json",
2039 success : $.proxy(this.load_notebook_success,this),
2156 success : $.proxy(this.load_notebook_success,this),
2040 error : $.proxy(this.load_notebook_error,this),
2157 error : $.proxy(this.load_notebook_error,this),
2041 };
2158 };
2042 $([IPython.events]).trigger('notebook_loading.Notebook');
2159 $([Events]).trigger('notebook_loading.Notebook');
2043 var url = utils.url_join_encode(
2160 var url = utils.url_join_encode(
2044 this.base_url,
2161 this.base_url,
2045 'api/notebooks',
2162 'api/notebooks',
2046 this.notebook_path,
2163 this.notebook_path,
2047 this.notebook_name
2164 this.notebook_name
2048 );
2165 );
2049 $.ajax(url, settings);
2166 $.ajax(url, settings);
2050 };
2167 };
2051
2168
2052 /**
2169 /**
2053 * Success callback for loading a notebook from the server.
2170 * Success callback for loading a notebook from the server.
2054 *
2171 *
2055 * Load notebook data from the JSON response.
2172 * Load notebook data from the JSON response.
2056 *
2173 *
2057 * @method load_notebook_success
2174 * @method load_notebook_success
2058 * @param {Object} data JSON representation of a notebook
2175 * @param {Object} data JSON representation of a notebook
2059 * @param {String} status Description of response status
2176 * @param {String} status Description of response status
2060 * @param {jqXHR} xhr jQuery Ajax object
2177 * @param {jqXHR} xhr jQuery Ajax object
2061 */
2178 */
2062 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
2179 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
2063 this.fromJSON(data);
2180 this.fromJSON(data);
2064 if (this.ncells() === 0) {
2181 if (this.ncells() === 0) {
2065 this.insert_cell_below('code');
2182 this.insert_cell_below('code');
2066 this.edit_mode(0);
2183 this.edit_mode(0);
2067 } else {
2184 } else {
2068 this.select(0);
2185 this.select(0);
2069 this.handle_command_mode(this.get_cell(0));
2186 this.handle_command_mode(this.get_cell(0));
2070 }
2187 }
2071 this.set_dirty(false);
2188 this.set_dirty(false);
2072 this.scroll_to_top();
2189 this.scroll_to_top();
2073 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
2190 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
2074 var msg = "This notebook has been converted from an older " +
2191 var msg = "This notebook has been converted from an older " +
2075 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
2192 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
2076 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
2193 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
2077 "newer notebook format will be used and older versions of IPython " +
2194 "newer notebook format will be used and older versions of IPython " +
2078 "may not be able to read it. To keep the older version, close the " +
2195 "may not be able to read it. To keep the older version, close the " +
2079 "notebook without saving it.";
2196 "notebook without saving it.";
2080 IPython.dialog.modal({
2197 Dialog.modal({
2081 title : "Notebook converted",
2198 title : "Notebook converted",
2082 body : msg,
2199 body : msg,
2083 buttons : {
2200 buttons : {
2084 OK : {
2201 OK : {
2085 class : "btn-primary"
2202 class : "btn-primary"
2086 }
2203 }
2087 }
2204 }
2088 });
2205 });
2089 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
2206 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
2090 var that = this;
2207 var that = this;
2091 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
2208 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
2092 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
2209 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
2093 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
2210 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
2094 this_vs + ". You can still work with this notebook, but some features " +
2211 this_vs + ". You can still work with this notebook, but some features " +
2095 "introduced in later notebook versions may not be available.";
2212 "introduced in later notebook versions may not be available.";
2096
2213
2097 IPython.dialog.modal({
2214 Dialog.modal({
2098 title : "Newer Notebook",
2215 title : "Newer Notebook",
2099 body : msg,
2216 body : msg,
2100 buttons : {
2217 buttons : {
2101 OK : {
2218 OK : {
2102 class : "btn-danger"
2219 class : "btn-danger"
2103 }
2220 }
2104 }
2221 }
2105 });
2222 });
2106
2223
2107 }
2224 }
2108
2225
2109 // Create the session after the notebook is completely loaded to prevent
2226 // Create the session after the notebook is completely loaded to prevent
2110 // code execution upon loading, which is a security risk.
2227 // code execution upon loading, which is a security risk.
2111 if (this.session === null) {
2228 if (this.session === null) {
2112 this.start_session();
2229 this.start_session();
2113 }
2230 }
2114 // load our checkpoint list
2231 // load our checkpoint list
2115 this.list_checkpoints();
2232 this.list_checkpoints();
2116
2233
2117 // load toolbar state
2234 // load toolbar state
2118 if (this.metadata.celltoolbar) {
2235 if (this.metadata.celltoolbar) {
2119 IPython.CellToolbar.global_show();
2236 CellToolbar.global_show();
2120 IPython.CellToolbar.activate_preset(this.metadata.celltoolbar);
2237 CellToolbar.activate_preset(this.metadata.celltoolbar);
2121 } else {
2238 } else {
2122 IPython.CellToolbar.global_hide();
2239 CellToolbar.global_hide();
2123 }
2240 }
2124
2241
2125 // now that we're fully loaded, it is safe to restore save functionality
2242 // now that we're fully loaded, it is safe to restore save functionality
2126 delete(this.save_notebook);
2243 delete(this.save_notebook);
2127 $([IPython.events]).trigger('notebook_loaded.Notebook');
2244 $([Events]).trigger('notebook_loaded.Notebook');
2128 };
2245 };
2129
2246
2130 /**
2247 /**
2131 * Failure callback for loading a notebook from the server.
2248 * Failure callback for loading a notebook from the server.
2132 *
2249 *
2133 * @method load_notebook_error
2250 * @method load_notebook_error
2134 * @param {jqXHR} xhr jQuery Ajax object
2251 * @param {jqXHR} xhr jQuery Ajax object
2135 * @param {String} status Description of response status
2252 * @param {String} status Description of response status
2136 * @param {String} error HTTP error message
2253 * @param {String} error HTTP error message
2137 */
2254 */
2138 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
2255 Notebook.prototype.load_notebook_error = function (xhr, status, error) {
2139 $([IPython.events]).trigger('notebook_load_failed.Notebook', [xhr, status, error]);
2256 $([Events]).trigger('notebook_load_failed.Notebook', [xhr, status, error]);
2140 var msg;
2257 var msg;
2141 if (xhr.status === 400) {
2258 if (xhr.status === 400) {
2142 msg = error;
2259 msg = error;
2143 } else if (xhr.status === 500) {
2260 } else if (xhr.status === 500) {
2144 msg = "An unknown error occurred while loading this notebook. " +
2261 msg = "An unknown error occurred while loading this notebook. " +
2145 "This version can load notebook formats " +
2262 "This version can load notebook formats " +
2146 "v" + this.nbformat + " or earlier.";
2263 "v" + this.nbformat + " or earlier.";
2147 }
2264 }
2148 IPython.dialog.modal({
2265 Dialog.modal({
2149 title: "Error loading notebook",
2266 title: "Error loading notebook",
2150 body : msg,
2267 body : msg,
2151 buttons : {
2268 buttons : {
2152 "OK": {}
2269 "OK": {}
2153 }
2270 }
2154 });
2271 });
2155 };
2272 };
2156
2273
2157 /********************* checkpoint-related *********************/
2274 /********************* checkpoint-related *********************/
2158
2275
2159 /**
2276 /**
2160 * Save the notebook then immediately create a checkpoint.
2277 * Save the notebook then immediately create a checkpoint.
2161 *
2278 *
2162 * @method save_checkpoint
2279 * @method save_checkpoint
2163 */
2280 */
2164 Notebook.prototype.save_checkpoint = function () {
2281 Notebook.prototype.save_checkpoint = function () {
2165 this._checkpoint_after_save = true;
2282 this._checkpoint_after_save = true;
2166 this.save_notebook();
2283 this.save_notebook();
2167 };
2284 };
2168
2285
2169 /**
2286 /**
2170 * Add a checkpoint for this notebook.
2287 * Add a checkpoint for this notebook.
2171 * for use as a callback from checkpoint creation.
2288 * for use as a callback from checkpoint creation.
2172 *
2289 *
2173 * @method add_checkpoint
2290 * @method add_checkpoint
2174 */
2291 */
2175 Notebook.prototype.add_checkpoint = function (checkpoint) {
2292 Notebook.prototype.add_checkpoint = function (checkpoint) {
2176 var found = false;
2293 var found = false;
2177 for (var i = 0; i < this.checkpoints.length; i++) {
2294 for (var i = 0; i < this.checkpoints.length; i++) {
2178 var existing = this.checkpoints[i];
2295 var existing = this.checkpoints[i];
2179 if (existing.id == checkpoint.id) {
2296 if (existing.id == checkpoint.id) {
2180 found = true;
2297 found = true;
2181 this.checkpoints[i] = checkpoint;
2298 this.checkpoints[i] = checkpoint;
2182 break;
2299 break;
2183 }
2300 }
2184 }
2301 }
2185 if (!found) {
2302 if (!found) {
2186 this.checkpoints.push(checkpoint);
2303 this.checkpoints.push(checkpoint);
2187 }
2304 }
2188 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2305 this.last_checkpoint = this.checkpoints[this.checkpoints.length - 1];
2189 };
2306 };
2190
2307
2191 /**
2308 /**
2192 * List checkpoints for this notebook.
2309 * List checkpoints for this notebook.
2193 *
2310 *
2194 * @method list_checkpoints
2311 * @method list_checkpoints
2195 */
2312 */
2196 Notebook.prototype.list_checkpoints = function () {
2313 Notebook.prototype.list_checkpoints = function () {
2197 var url = utils.url_join_encode(
2314 var url = utils.url_join_encode(
2198 this.base_url,
2315 this.base_url,
2199 'api/notebooks',
2316 'api/notebooks',
2200 this.notebook_path,
2317 this.notebook_path,
2201 this.notebook_name,
2318 this.notebook_name,
2202 'checkpoints'
2319 'checkpoints'
2203 );
2320 );
2204 $.get(url).done(
2321 $.get(url).done(
2205 $.proxy(this.list_checkpoints_success, this)
2322 $.proxy(this.list_checkpoints_success, this)
2206 ).fail(
2323 ).fail(
2207 $.proxy(this.list_checkpoints_error, this)
2324 $.proxy(this.list_checkpoints_error, this)
2208 );
2325 );
2209 };
2326 };
2210
2327
2211 /**
2328 /**
2212 * Success callback for listing checkpoints.
2329 * Success callback for listing checkpoints.
2213 *
2330 *
2214 * @method list_checkpoint_success
2331 * @method list_checkpoint_success
2215 * @param {Object} data JSON representation of a checkpoint
2332 * @param {Object} data JSON representation of a checkpoint
2216 * @param {String} status Description of response status
2333 * @param {String} status Description of response status
2217 * @param {jqXHR} xhr jQuery Ajax object
2334 * @param {jqXHR} xhr jQuery Ajax object
2218 */
2335 */
2219 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2336 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
2220 data = $.parseJSON(data);
2337 data = $.parseJSON(data);
2221 this.checkpoints = data;
2338 this.checkpoints = data;
2222 if (data.length) {
2339 if (data.length) {
2223 this.last_checkpoint = data[data.length - 1];
2340 this.last_checkpoint = data[data.length - 1];
2224 } else {
2341 } else {
2225 this.last_checkpoint = null;
2342 this.last_checkpoint = null;
2226 }
2343 }
2227 $([IPython.events]).trigger('checkpoints_listed.Notebook', [data]);
2344 $([Events]).trigger('checkpoints_listed.Notebook', [data]);
2228 };
2345 };
2229
2346
2230 /**
2347 /**
2231 * Failure callback for listing a checkpoint.
2348 * Failure callback for listing a checkpoint.
2232 *
2349 *
2233 * @method list_checkpoint_error
2350 * @method list_checkpoint_error
2234 * @param {jqXHR} xhr jQuery Ajax object
2351 * @param {jqXHR} xhr jQuery Ajax object
2235 * @param {String} status Description of response status
2352 * @param {String} status Description of response status
2236 * @param {String} error_msg HTTP error message
2353 * @param {String} error_msg HTTP error message
2237 */
2354 */
2238 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2355 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
2239 $([IPython.events]).trigger('list_checkpoints_failed.Notebook');
2356 $([Events]).trigger('list_checkpoints_failed.Notebook');
2240 };
2357 };
2241
2358
2242 /**
2359 /**
2243 * Create a checkpoint of this notebook on the server from the most recent save.
2360 * Create a checkpoint of this notebook on the server from the most recent save.
2244 *
2361 *
2245 * @method create_checkpoint
2362 * @method create_checkpoint
2246 */
2363 */
2247 Notebook.prototype.create_checkpoint = function () {
2364 Notebook.prototype.create_checkpoint = function () {
2248 var url = utils.url_join_encode(
2365 var url = utils.url_join_encode(
2249 this.base_url,
2366 this.base_url,
2250 'api/notebooks',
2367 'api/notebooks',
2251 this.notebook_path,
2368 this.notebook_path,
2252 this.notebook_name,
2369 this.notebook_name,
2253 'checkpoints'
2370 'checkpoints'
2254 );
2371 );
2255 $.post(url).done(
2372 $.post(url).done(
2256 $.proxy(this.create_checkpoint_success, this)
2373 $.proxy(this.create_checkpoint_success, this)
2257 ).fail(
2374 ).fail(
2258 $.proxy(this.create_checkpoint_error, this)
2375 $.proxy(this.create_checkpoint_error, this)
2259 );
2376 );
2260 };
2377 };
2261
2378
2262 /**
2379 /**
2263 * Success callback for creating a checkpoint.
2380 * Success callback for creating a checkpoint.
2264 *
2381 *
2265 * @method create_checkpoint_success
2382 * @method create_checkpoint_success
2266 * @param {Object} data JSON representation of a checkpoint
2383 * @param {Object} data JSON representation of a checkpoint
2267 * @param {String} status Description of response status
2384 * @param {String} status Description of response status
2268 * @param {jqXHR} xhr jQuery Ajax object
2385 * @param {jqXHR} xhr jQuery Ajax object
2269 */
2386 */
2270 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2387 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
2271 data = $.parseJSON(data);
2388 data = $.parseJSON(data);
2272 this.add_checkpoint(data);
2389 this.add_checkpoint(data);
2273 $([IPython.events]).trigger('checkpoint_created.Notebook', data);
2390 $([Events]).trigger('checkpoint_created.Notebook', data);
2274 };
2391 };
2275
2392
2276 /**
2393 /**
2277 * Failure callback for creating a checkpoint.
2394 * Failure callback for creating a checkpoint.
2278 *
2395 *
2279 * @method create_checkpoint_error
2396 * @method create_checkpoint_error
2280 * @param {jqXHR} xhr jQuery Ajax object
2397 * @param {jqXHR} xhr jQuery Ajax object
2281 * @param {String} status Description of response status
2398 * @param {String} status Description of response status
2282 * @param {String} error_msg HTTP error message
2399 * @param {String} error_msg HTTP error message
2283 */
2400 */
2284 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2401 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
2285 $([IPython.events]).trigger('checkpoint_failed.Notebook');
2402 $([Events]).trigger('checkpoint_failed.Notebook');
2286 };
2403 };
2287
2404
2288 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2405 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
2289 var that = this;
2406 var that = this;
2290 checkpoint = checkpoint || this.last_checkpoint;
2407 checkpoint = checkpoint || this.last_checkpoint;
2291 if ( ! checkpoint ) {
2408 if ( ! checkpoint ) {
2292 console.log("restore dialog, but no checkpoint to restore to!");
2409 console.log("restore dialog, but no checkpoint to restore to!");
2293 return;
2410 return;
2294 }
2411 }
2295 var body = $('<div/>').append(
2412 var body = $('<div/>').append(
2296 $('<p/>').addClass("p-space").text(
2413 $('<p/>').addClass("p-space").text(
2297 "Are you sure you want to revert the notebook to " +
2414 "Are you sure you want to revert the notebook to " +
2298 "the latest checkpoint?"
2415 "the latest checkpoint?"
2299 ).append(
2416 ).append(
2300 $("<strong/>").text(
2417 $("<strong/>").text(
2301 " This cannot be undone."
2418 " This cannot be undone."
2302 )
2419 )
2303 )
2420 )
2304 ).append(
2421 ).append(
2305 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2422 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
2306 ).append(
2423 ).append(
2307 $('<p/>').addClass("p-space").text(
2424 $('<p/>').addClass("p-space").text(
2308 Date(checkpoint.last_modified)
2425 Date(checkpoint.last_modified)
2309 ).css("text-align", "center")
2426 ).css("text-align", "center")
2310 );
2427 );
2311
2428
2312 IPython.dialog.modal({
2429 Dialog.modal({
2313 title : "Revert notebook to checkpoint",
2430 title : "Revert notebook to checkpoint",
2314 body : body,
2431 body : body,
2315 buttons : {
2432 buttons : {
2316 Revert : {
2433 Revert : {
2317 class : "btn-danger",
2434 class : "btn-danger",
2318 click : function () {
2435 click : function () {
2319 that.restore_checkpoint(checkpoint.id);
2436 that.restore_checkpoint(checkpoint.id);
2320 }
2437 }
2321 },
2438 },
2322 Cancel : {}
2439 Cancel : {}
2323 }
2440 }
2324 });
2441 });
2325 };
2442 };
2326
2443
2327 /**
2444 /**
2328 * Restore the notebook to a checkpoint state.
2445 * Restore the notebook to a checkpoint state.
2329 *
2446 *
2330 * @method restore_checkpoint
2447 * @method restore_checkpoint
2331 * @param {String} checkpoint ID
2448 * @param {String} checkpoint ID
2332 */
2449 */
2333 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2450 Notebook.prototype.restore_checkpoint = function (checkpoint) {
2334 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
2451 $([Events]).trigger('notebook_restoring.Notebook', checkpoint);
2335 var url = utils.url_join_encode(
2452 var url = utils.url_join_encode(
2336 this.base_url,
2453 this.base_url,
2337 'api/notebooks',
2454 'api/notebooks',
2338 this.notebook_path,
2455 this.notebook_path,
2339 this.notebook_name,
2456 this.notebook_name,
2340 'checkpoints',
2457 'checkpoints',
2341 checkpoint
2458 checkpoint
2342 );
2459 );
2343 $.post(url).done(
2460 $.post(url).done(
2344 $.proxy(this.restore_checkpoint_success, this)
2461 $.proxy(this.restore_checkpoint_success, this)
2345 ).fail(
2462 ).fail(
2346 $.proxy(this.restore_checkpoint_error, this)
2463 $.proxy(this.restore_checkpoint_error, this)
2347 );
2464 );
2348 };
2465 };
2349
2466
2350 /**
2467 /**
2351 * Success callback for restoring a notebook to a checkpoint.
2468 * Success callback for restoring a notebook to a checkpoint.
2352 *
2469 *
2353 * @method restore_checkpoint_success
2470 * @method restore_checkpoint_success
2354 * @param {Object} data (ignored, should be empty)
2471 * @param {Object} data (ignored, should be empty)
2355 * @param {String} status Description of response status
2472 * @param {String} status Description of response status
2356 * @param {jqXHR} xhr jQuery Ajax object
2473 * @param {jqXHR} xhr jQuery Ajax object
2357 */
2474 */
2358 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2475 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
2359 $([IPython.events]).trigger('checkpoint_restored.Notebook');
2476 $([Events]).trigger('checkpoint_restored.Notebook');
2360 this.load_notebook(this.notebook_name, this.notebook_path);
2477 this.load_notebook(this.notebook_name, this.notebook_path);
2361 };
2478 };
2362
2479
2363 /**
2480 /**
2364 * Failure callback for restoring a notebook to a checkpoint.
2481 * Failure callback for restoring a notebook to a checkpoint.
2365 *
2482 *
2366 * @method restore_checkpoint_error
2483 * @method restore_checkpoint_error
2367 * @param {jqXHR} xhr jQuery Ajax object
2484 * @param {jqXHR} xhr jQuery Ajax object
2368 * @param {String} status Description of response status
2485 * @param {String} status Description of response status
2369 * @param {String} error_msg HTTP error message
2486 * @param {String} error_msg HTTP error message
2370 */
2487 */
2371 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2488 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
2372 $([IPython.events]).trigger('checkpoint_restore_failed.Notebook');
2489 $([Events]).trigger('checkpoint_restore_failed.Notebook');
2373 };
2490 };
2374
2491
2375 /**
2492 /**
2376 * Delete a notebook checkpoint.
2493 * Delete a notebook checkpoint.
2377 *
2494 *
2378 * @method delete_checkpoint
2495 * @method delete_checkpoint
2379 * @param {String} checkpoint ID
2496 * @param {String} checkpoint ID
2380 */
2497 */
2381 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2498 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2382 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
2499 $([Events]).trigger('notebook_restoring.Notebook', checkpoint);
2383 var url = utils.url_join_encode(
2500 var url = utils.url_join_encode(
2384 this.base_url,
2501 this.base_url,
2385 'api/notebooks',
2502 'api/notebooks',
2386 this.notebook_path,
2503 this.notebook_path,
2387 this.notebook_name,
2504 this.notebook_name,
2388 'checkpoints',
2505 'checkpoints',
2389 checkpoint
2506 checkpoint
2390 );
2507 );
2391 $.ajax(url, {
2508 $.ajax(url, {
2392 type: 'DELETE',
2509 type: 'DELETE',
2393 success: $.proxy(this.delete_checkpoint_success, this),
2510 success: $.proxy(this.delete_checkpoint_success, this),
2394 error: $.proxy(this.delete_checkpoint_error, this)
2511 error: $.proxy(this.delete_checkpoint_error, this)
2395 });
2512 });
2396 };
2513 };
2397
2514
2398 /**
2515 /**
2399 * Success callback for deleting a notebook checkpoint
2516 * Success callback for deleting a notebook checkpoint
2400 *
2517 *
2401 * @method delete_checkpoint_success
2518 * @method delete_checkpoint_success
2402 * @param {Object} data (ignored, should be empty)
2519 * @param {Object} data (ignored, should be empty)
2403 * @param {String} status Description of response status
2520 * @param {String} status Description of response status
2404 * @param {jqXHR} xhr jQuery Ajax object
2521 * @param {jqXHR} xhr jQuery Ajax object
2405 */
2522 */
2406 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2523 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2407 $([IPython.events]).trigger('checkpoint_deleted.Notebook', data);
2524 $([Events]).trigger('checkpoint_deleted.Notebook', data);
2408 this.load_notebook(this.notebook_name, this.notebook_path);
2525 this.load_notebook(this.notebook_name, this.notebook_path);
2409 };
2526 };
2410
2527
2411 /**
2528 /**
2412 * Failure callback for deleting a notebook checkpoint.
2529 * Failure callback for deleting a notebook checkpoint.
2413 *
2530 *
2414 * @method delete_checkpoint_error
2531 * @method delete_checkpoint_error
2415 * @param {jqXHR} xhr jQuery Ajax object
2532 * @param {jqXHR} xhr jQuery Ajax object
2416 * @param {String} status Description of response status
2533 * @param {String} status Description of response status
2417 * @param {String} error_msg HTTP error message
2534 * @param {String} error_msg HTTP error message
2418 */
2535 */
2419 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2536 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2420 $([IPython.events]).trigger('checkpoint_delete_failed.Notebook');
2537 $([Events]).trigger('checkpoint_delete_failed.Notebook');
2421 };
2538 };
2422
2539
2423
2540
2541 // For backwards compatability.
2424 IPython.Notebook = Notebook;
2542 IPython.Notebook = Notebook;
2425
2543
2426
2544 return Notebook;
2427 return IPython;
2545 });
2428
2429 }(IPython));
2430
@@ -1,458 +1,459 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Copyright (C) 2008-2012 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 // TextCell
9 // TextCell
10 //============================================================================
10 //============================================================================
11
11
12
12
13
13
14 /**
14 /**
15 A module that allow to create different type of Text Cell
15 A module that allow to create different type of Text Cell
16 @module IPython
16 @module IPython
17 @namespace IPython
17 @namespace IPython
18 */
18 */
19 var IPython = (function (IPython) {
19 var IPython = (function (IPython) {
20 "use strict";
20 "use strict";
21
21
22 // TextCell base class
22 // TextCell base class
23 var keycodes = IPython.keyboard.keycodes;
23 var keycodes = IPython.keyboard.keycodes;
24 var security = IPython.security;
24 var security = IPython.security;
25
25
26 /**
26 /**
27 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
27 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
28 * cell start as not redered.
28 * cell start as not redered.
29 *
29 *
30 * @class TextCell
30 * @class TextCell
31 * @constructor TextCell
31 * @constructor TextCell
32 * @extend IPython.Cell
32 * @extend IPython.Cell
33 * @param {object|undefined} [options]
33 * @param {object|undefined} [options]
34 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config
34 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config
35 * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass)
35 * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass)
36 */
36 */
37 var TextCell = function (options) {
37 var TextCell = function (options) {
38 // in all TextCell/Cell subclasses
38 // in all TextCell/Cell subclasses
39 // do not assign most of members here, just pass it down
39 // do not assign most of members here, just pass it down
40 // in the options dict potentially overwriting what you wish.
40 // in the options dict potentially overwriting what you wish.
41 // they will be assigned in the base class.
41 // they will be assigned in the base class.
42
42
43 // we cannot put this as a class key as it has handle to "this".
43 // we cannot put this as a class key as it has handle to "this".
44 var cm_overwrite_options = {
44 var cm_overwrite_options = {
45 onKeyEvent: $.proxy(this.handle_keyevent,this)
45 onKeyEvent: $.proxy(this.handle_keyevent,this)
46 };
46 };
47
47
48 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
48 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
49
49
50 this.cell_type = this.cell_type || 'text';
50 this.cell_type = this.cell_type || 'text';
51
51
52 IPython.Cell.apply(this, [options]);
52 IPython.Cell.apply(this, [options]);
53
53
54 this.rendered = false;
54 this.rendered = false;
55 };
55 };
56
56
57 TextCell.prototype = new IPython.Cell();
57 TextCell.prototype = new IPython.Cell();
58
58
59 TextCell.options_default = {
59 TextCell.options_default = {
60 cm_config : {
60 cm_config : {
61 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
61 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
62 mode: 'htmlmixed',
62 mode: 'htmlmixed',
63 lineWrapping : true,
63 lineWrapping : true,
64 }
64 }
65 };
65 };
66
66
67
67
68 /**
68 /**
69 * Create the DOM element of the TextCell
69 * Create the DOM element of the TextCell
70 * @method create_element
70 * @method create_element
71 * @private
71 * @private
72 */
72 */
73 TextCell.prototype.create_element = function () {
73 TextCell.prototype.create_element = function () {
74 IPython.Cell.prototype.create_element.apply(this, arguments);
74 IPython.Cell.prototype.create_element.apply(this, arguments);
75
75
76 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
76 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
77 cell.attr('tabindex','2');
77 cell.attr('tabindex','2');
78
78
79 var prompt = $('<div/>').addClass('prompt input_prompt');
79 var prompt = $('<div/>').addClass('prompt input_prompt');
80 cell.append(prompt);
80 cell.append(prompt);
81 var inner_cell = $('<div/>').addClass('inner_cell');
81 var inner_cell = $('<div/>').addClass('inner_cell');
82 this.celltoolbar = new IPython.CellToolbar(this);
82 this.celltoolbar = new IPython.CellToolbar(this);
83 inner_cell.append(this.celltoolbar.element);
83 inner_cell.append(this.celltoolbar.element);
84 var input_area = $('<div/>').addClass('input_area');
84 var input_area = $('<div/>').addClass('input_area');
85 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
85 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
86 // The tabindex=-1 makes this div focusable.
86 // The tabindex=-1 makes this div focusable.
87 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
87 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
88 addClass('rendered_html').attr('tabindex','-1');
88 addClass('rendered_html').attr('tabindex','-1');
89 inner_cell.append(input_area).append(render_area);
89 inner_cell.append(input_area).append(render_area);
90 cell.append(inner_cell);
90 cell.append(inner_cell);
91 this.element = cell;
91 this.element = cell;
92 };
92 };
93
93
94
94
95 /**
95 /**
96 * Bind the DOM evet to cell actions
96 * Bind the DOM evet to cell actions
97 * Need to be called after TextCell.create_element
97 * Need to be called after TextCell.create_element
98 * @private
98 * @private
99 * @method bind_event
99 * @method bind_event
100 */
100 */
101 TextCell.prototype.bind_events = function () {
101 TextCell.prototype.bind_events = function () {
102 IPython.Cell.prototype.bind_events.apply(this);
102 IPython.Cell.prototype.bind_events.apply(this);
103 var that = this;
103 var that = this;
104
104
105 this.element.dblclick(function () {
105 this.element.dblclick(function () {
106 if (that.selected === false) {
106 if (that.selected === false) {
107 $([IPython.events]).trigger('select.Cell', {'cell':that});
107 $([IPython.events]).trigger('select.Cell', {'cell':that});
108 }
108 }
109 var cont = that.unrender();
109 var cont = that.unrender();
110 if (cont) {
110 if (cont) {
111 that.focus_editor();
111 that.focus_editor();
112 }
112 }
113 });
113 });
114 };
114 };
115
115
116 // Cell level actions
116 // Cell level actions
117
117
118 TextCell.prototype.select = function () {
118 TextCell.prototype.select = function () {
119 var cont = IPython.Cell.prototype.select.apply(this);
119 var cont = IPython.Cell.prototype.select.apply(this);
120 if (cont) {
120 if (cont) {
121 if (this.mode === 'edit') {
121 if (this.mode === 'edit') {
122 this.code_mirror.refresh();
122 this.code_mirror.refresh();
123 }
123 }
124 }
124 }
125 return cont;
125 return cont;
126 };
126 };
127
127
128 TextCell.prototype.unrender = function () {
128 TextCell.prototype.unrender = function () {
129 if (this.read_only) return;
129 if (this.read_only) return;
130 var cont = IPython.Cell.prototype.unrender.apply(this);
130 var cont = IPython.Cell.prototype.unrender.apply(this);
131 if (cont) {
131 if (cont) {
132 var text_cell = this.element;
132 var text_cell = this.element;
133 var output = text_cell.find("div.text_cell_render");
133 var output = text_cell.find("div.text_cell_render");
134 output.hide();
134 output.hide();
135 text_cell.find('div.input_area').show();
135 text_cell.find('div.input_area').show();
136 if (this.get_text() === this.placeholder) {
136 if (this.get_text() === this.placeholder) {
137 this.set_text('');
137 this.set_text('');
138 }
138 }
139 this.refresh();
139 this.refresh();
140 }
140 }
141 if (this.celltoolbar.ui_controls_list.length) {
141 if (this.celltoolbar.ui_controls_list.length) {
142 this.celltoolbar.show();
142 this.celltoolbar.show();
143 }
143 }
144 return cont;
144 return cont;
145 };
145 };
146
146
147 TextCell.prototype.execute = function () {
147 TextCell.prototype.execute = function () {
148 this.render();
148 this.render();
149 };
149 };
150
150
151 /**
151 /**
152 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
152 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
153 * @method get_text
153 * @method get_text
154 * @retrun {string} CodeMirror current text value
154 * @retrun {string} CodeMirror current text value
155 */
155 */
156 TextCell.prototype.get_text = function() {
156 TextCell.prototype.get_text = function() {
157 return this.code_mirror.getValue();
157 return this.code_mirror.getValue();
158 };
158 };
159
159
160 /**
160 /**
161 * @param {string} text - Codemiror text value
161 * @param {string} text - Codemiror text value
162 * @see TextCell#get_text
162 * @see TextCell#get_text
163 * @method set_text
163 * @method set_text
164 * */
164 * */
165 TextCell.prototype.set_text = function(text) {
165 TextCell.prototype.set_text = function(text) {
166 this.code_mirror.setValue(text);
166 this.code_mirror.setValue(text);
167 this.code_mirror.refresh();
167 this.code_mirror.refresh();
168 };
168 };
169
169
170 /**
170 /**
171 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
171 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
172 * @method get_rendered
172 * @method get_rendered
173 * @return {html} html of rendered element
173 * @return {html} html of rendered element
174 * */
174 * */
175 TextCell.prototype.get_rendered = function() {
175 TextCell.prototype.get_rendered = function() {
176 return this.element.find('div.text_cell_render').html();
176 return this.element.find('div.text_cell_render').html();
177 };
177 };
178
178
179 /**
179 /**
180 * @method set_rendered
180 * @method set_rendered
181 */
181 */
182 TextCell.prototype.set_rendered = function(text) {
182 TextCell.prototype.set_rendered = function(text) {
183 this.element.find('div.text_cell_render').html(text);
183 this.element.find('div.text_cell_render').html(text);
184 this.celltoolbar.hide();
184 this.celltoolbar.hide();
185 };
185 };
186
186
187
187
188 /**
188 /**
189 * Create Text cell from JSON
189 * Create Text cell from JSON
190 * @param {json} data - JSON serialized text-cell
190 * @param {json} data - JSON serialized text-cell
191 * @method fromJSON
191 * @method fromJSON
192 */
192 */
193 TextCell.prototype.fromJSON = function (data) {
193 TextCell.prototype.fromJSON = function (data) {
194 IPython.Cell.prototype.fromJSON.apply(this, arguments);
194 IPython.Cell.prototype.fromJSON.apply(this, arguments);
195 if (data.cell_type === this.cell_type) {
195 if (data.cell_type === this.cell_type) {
196 if (data.source !== undefined) {
196 if (data.source !== undefined) {
197 this.set_text(data.source);
197 this.set_text(data.source);
198 // make this value the starting point, so that we can only undo
198 // make this value the starting point, so that we can only undo
199 // to this state, instead of a blank cell
199 // to this state, instead of a blank cell
200 this.code_mirror.clearHistory();
200 this.code_mirror.clearHistory();
201 // TODO: This HTML needs to be treated as potentially dangerous
201 // TODO: This HTML needs to be treated as potentially dangerous
202 // user input and should be handled before set_rendered.
202 // user input and should be handled before set_rendered.
203 this.set_rendered(data.rendered || '');
203 this.set_rendered(data.rendered || '');
204 this.rendered = false;
204 this.rendered = false;
205 this.render();
205 this.render();
206 }
206 }
207 }
207 }
208 };
208 };
209
209
210 /** Generate JSON from cell
210 /** Generate JSON from cell
211 * @return {object} cell data serialised to json
211 * @return {object} cell data serialised to json
212 */
212 */
213 TextCell.prototype.toJSON = function () {
213 TextCell.prototype.toJSON = function () {
214 var data = IPython.Cell.prototype.toJSON.apply(this);
214 var data = IPython.Cell.prototype.toJSON.apply(this);
215 data.source = this.get_text();
215 data.source = this.get_text();
216 if (data.source == this.placeholder) {
216 if (data.source == this.placeholder) {
217 data.source = "";
217 data.source = "";
218 }
218 }
219 return data;
219 return data;
220 };
220 };
221
221
222
222
223 /**
223 /**
224 * @class MarkdownCell
224 * @class MarkdownCell
225 * @constructor MarkdownCell
225 * @constructor MarkdownCell
226 * @extends IPython.HTMLCell
226 * @extends IPython.HTMLCell
227 */
227 */
228 var MarkdownCell = function (options) {
228 var MarkdownCell = function (options) {
229 options = this.mergeopt(MarkdownCell, options);
229 options = this.mergeopt(MarkdownCell, options);
230
230
231 this.cell_type = 'markdown';
231 this.cell_type = 'markdown';
232 TextCell.apply(this, [options]);
232 TextCell.apply(this, [options]);
233 };
233 };
234
234
235 MarkdownCell.options_default = {
235 MarkdownCell.options_default = {
236 cm_config: {
236 cm_config: {
237 mode: 'ipythongfm'
237 mode: 'ipythongfm'
238 },
238 },
239 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
239 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
240 };
240 };
241
241
242 MarkdownCell.prototype = new TextCell();
242 MarkdownCell.prototype = new TextCell();
243
243
244 /**
244 /**
245 * @method render
245 * @method render
246 */
246 */
247 MarkdownCell.prototype.render = function () {
247 MarkdownCell.prototype.render = function () {
248 var cont = IPython.TextCell.prototype.render.apply(this);
248 var cont = IPython.TextCell.prototype.render.apply(this);
249 if (cont) {
249 if (cont) {
250 var text = this.get_text();
250 var text = this.get_text();
251 var math = null;
251 var math = null;
252 if (text === "") { text = this.placeholder; }
252 if (text === "") { text = this.placeholder; }
253 var text_and_math = IPython.mathjaxutils.remove_math(text);
253 var text_and_math = IPython.mathjaxutils.remove_math(text);
254 text = text_and_math[0];
254 text = text_and_math[0];
255 math = text_and_math[1];
255 math = text_and_math[1];
256 var html = marked.parser(marked.lexer(text));
256 var html = marked.parser(marked.lexer(text));
257 html = IPython.mathjaxutils.replace_math(html, math);
257 html = IPython.mathjaxutils.replace_math(html, math);
258 html = security.sanitize_html(html);
258 html = security.sanitize_html(html);
259 html = $($.parseHTML(html));
259 html = $($.parseHTML(html));
260 // links in markdown cells should open in new tabs
260 // links in markdown cells should open in new tabs
261 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
261 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
262 this.set_rendered(html);
262 this.set_rendered(html);
263 this.element.find('div.input_area').hide();
263 this.element.find('div.input_area').hide();
264 this.element.find("div.text_cell_render").show();
264 this.element.find("div.text_cell_render").show();
265 this.typeset();
265 this.typeset();
266 }
266 }
267 return cont;
267 return cont;
268 };
268 };
269
269
270
270
271 // RawCell
271 // RawCell
272
272
273 /**
273 /**
274 * @class RawCell
274 * @class RawCell
275 * @constructor RawCell
275 * @constructor RawCell
276 * @extends IPython.TextCell
276 * @extends IPython.TextCell
277 */
277 */
278 var RawCell = function (options) {
278 var RawCell = function (options) {
279
279
280 options = this.mergeopt(RawCell,options);
280 options = this.mergeopt(RawCell,options);
281 TextCell.apply(this, [options]);
281 TextCell.apply(this, [options]);
282 this.cell_type = 'raw';
282 this.cell_type = 'raw';
283 // RawCell should always hide its rendered div
283 // RawCell should always hide its rendered div
284 this.element.find('div.text_cell_render').hide();
284 this.element.find('div.text_cell_render').hide();
285 };
285 };
286
286
287 RawCell.options_default = {
287 RawCell.options_default = {
288 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
288 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " +
289 "It will not be rendered in the notebook. " +
289 "It will not be rendered in the notebook. " +
290 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
290 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
291 };
291 };
292
292
293 RawCell.prototype = new TextCell();
293 RawCell.prototype = new TextCell();
294
294
295 /** @method bind_events **/
295 /** @method bind_events **/
296 RawCell.prototype.bind_events = function () {
296 RawCell.prototype.bind_events = function () {
297 TextCell.prototype.bind_events.apply(this);
297 TextCell.prototype.bind_events.apply(this);
298 var that = this;
298 var that = this;
299 this.element.focusout(function() {
299 this.element.focusout(function() {
300 that.auto_highlight();
300 that.auto_highlight();
301 that.render();
301 that.render();
302 });
302 });
303
303
304 this.code_mirror.on('focus', function() { that.unrender(); });
304 this.code_mirror.on('focus', function() { that.unrender(); });
305 };
305 };
306
306
307 /**
307 /**
308 * Trigger autodetection of highlight scheme for current cell
308 * Trigger autodetection of highlight scheme for current cell
309 * @method auto_highlight
309 * @method auto_highlight
310 */
310 */
311 RawCell.prototype.auto_highlight = function () {
311 RawCell.prototype.auto_highlight = function () {
312 this._auto_highlight(IPython.config.raw_cell_highlight);
312 this._auto_highlight(IPython.config.raw_cell_highlight);
313 };
313 };
314
314
315 /** @method render **/
315 /** @method render **/
316 RawCell.prototype.render = function () {
316 RawCell.prototype.render = function () {
317 var cont = IPython.TextCell.prototype.render.apply(this);
317 var cont = IPython.TextCell.prototype.render.apply(this);
318 if (cont){
318 if (cont){
319 var text = this.get_text();
319 var text = this.get_text();
320 if (text === "") { text = this.placeholder; }
320 if (text === "") { text = this.placeholder; }
321 this.set_text(text);
321 this.set_text(text);
322 this.element.removeClass('rendered');
322 this.element.removeClass('rendered');
323 }
323 }
324 return cont;
324 return cont;
325 };
325 };
326
326
327
327
328 /**
328 /**
329 * @class HeadingCell
329 * @class HeadingCell
330 * @extends IPython.TextCell
330 * @extends IPython.TextCell
331 */
331 */
332
332
333 /**
333 /**
334 * @constructor HeadingCell
334 * @constructor HeadingCell
335 * @extends IPython.TextCell
335 * @extends IPython.TextCell
336 */
336 */
337 var HeadingCell = function (options) {
337 var HeadingCell = function (options) {
338 options = this.mergeopt(HeadingCell, options);
338 options = this.mergeopt(HeadingCell, options);
339
339
340 this.level = 1;
340 this.level = 1;
341 this.cell_type = 'heading';
341 this.cell_type = 'heading';
342 TextCell.apply(this, [options]);
342 TextCell.apply(this, [options]);
343
343
344 /**
344 /**
345 * heading level of the cell, use getter and setter to access
345 * heading level of the cell, use getter and setter to access
346 * @property level
346 * @property level
347 */
347 */
348 };
348 };
349
349
350 HeadingCell.options_default = {
350 HeadingCell.options_default = {
351 placeholder: "Type Heading Here"
351 placeholder: "Type Heading Here"
352 };
352 };
353
353
354 HeadingCell.prototype = new TextCell();
354 HeadingCell.prototype = new TextCell();
355
355
356 /** @method fromJSON */
356 /** @method fromJSON */
357 HeadingCell.prototype.fromJSON = function (data) {
357 HeadingCell.prototype.fromJSON = function (data) {
358 if (data.level !== undefined){
358 if (data.level !== undefined){
359 this.level = data.level;
359 this.level = data.level;
360 }
360 }
361 TextCell.prototype.fromJSON.apply(this, arguments);
361 TextCell.prototype.fromJSON.apply(this, arguments);
362 };
362 };
363
363
364
364
365 /** @method toJSON */
365 /** @method toJSON */
366 HeadingCell.prototype.toJSON = function () {
366 HeadingCell.prototype.toJSON = function () {
367 var data = TextCell.prototype.toJSON.apply(this);
367 var data = TextCell.prototype.toJSON.apply(this);
368 data.level = this.get_level();
368 data.level = this.get_level();
369 return data;
369 return data;
370 };
370 };
371
371
372 /**
372 /**
373 * can the cell be split into two cells
373 * can the cell be split into two cells
374 * @method is_splittable
374 * @method is_splittable
375 **/
375 **/
376 HeadingCell.prototype.is_splittable = function () {
376 HeadingCell.prototype.is_splittable = function () {
377 return false;
377 return false;
378 };
378 };
379
379
380
380
381 /**
381 /**
382 * can the cell be merged with other cells
382 * can the cell be merged with other cells
383 * @method is_mergeable
383 * @method is_mergeable
384 **/
384 **/
385 HeadingCell.prototype.is_mergeable = function () {
385 HeadingCell.prototype.is_mergeable = function () {
386 return false;
386 return false;
387 };
387 };
388
388
389 /**
389 /**
390 * Change heading level of cell, and re-render
390 * Change heading level of cell, and re-render
391 * @method set_level
391 * @method set_level
392 */
392 */
393 HeadingCell.prototype.set_level = function (level) {
393 HeadingCell.prototype.set_level = function (level) {
394 this.level = level;
394 this.level = level;
395 if (this.rendered) {
395 if (this.rendered) {
396 this.rendered = false;
396 this.rendered = false;
397 this.render();
397 this.render();
398 }
398 }
399 };
399 };
400
400
401 /** The depth of header cell, based on html (h1 to h6)
401 /** The depth of header cell, based on html (h1 to h6)
402 * @method get_level
402 * @method get_level
403 * @return {integer} level - for 1 to 6
403 * @return {integer} level - for 1 to 6
404 */
404 */
405 HeadingCell.prototype.get_level = function () {
405 HeadingCell.prototype.get_level = function () {
406 return this.level;
406 return this.level;
407 };
407 };
408
408
409
409
410 HeadingCell.prototype.get_rendered = function () {
410 HeadingCell.prototype.get_rendered = function () {
411 var r = this.element.find("div.text_cell_render");
411 var r = this.element.find("div.text_cell_render");
412 return r.children().first().html();
412 return r.children().first().html();
413 };
413 };
414
414
415
415
416 HeadingCell.prototype.render = function () {
416 HeadingCell.prototype.render = function () {
417 var cont = IPython.TextCell.prototype.render.apply(this);
417 var cont = IPython.TextCell.prototype.render.apply(this);
418 if (cont) {
418 if (cont) {
419 var text = this.get_text();
419 var text = this.get_text();
420 var math = null;
420 var math = null;
421 // Markdown headings must be a single line
421 // Markdown headings must be a single line
422 text = text.replace(/\n/g, ' ');
422 text = text.replace(/\n/g, ' ');
423 if (text === "") { text = this.placeholder; }
423 if (text === "") { text = this.placeholder; }
424 text = Array(this.level + 1).join("#") + " " + text;
424 text = Array(this.level + 1).join("#") + " " + text;
425 var text_and_math = IPython.mathjaxutils.remove_math(text);
425 var text_and_math = IPython.mathjaxutils.remove_math(text);
426 text = text_and_math[0];
426 text = text_and_math[0];
427 math = text_and_math[1];
427 math = text_and_math[1];
428 var html = marked.parser(marked.lexer(text));
428 var html = marked.parser(marked.lexer(text));
429 html = IPython.mathjaxutils.replace_math(html, math);
429 html = IPython.mathjaxutils.replace_math(html, math);
430 html = security.sanitize_html(html);
430 html = security.sanitize_html(html);
431 var h = $($.parseHTML(html));
431 var h = $($.parseHTML(html));
432 // add id and linkback anchor
432 // add id and linkback anchor
433 var hash = h.text().replace(/ /g, '-');
433 var hash = h.text().replace(/ /g, '-');
434 h.attr('id', hash);
434 h.attr('id', hash);
435 h.append(
435 h.append(
436 $('<a/>')
436 $('<a/>')
437 .addClass('anchor-link')
437 .addClass('anchor-link')
438 .attr('href', '#' + hash)
438 .attr('href', '#' + hash)
439 .text('ΒΆ')
439 .text('ΒΆ')
440 );
440 );
441 this.set_rendered(h);
441 this.set_rendered(h);
442 this.element.find('div.input_area').hide();
442 this.element.find('div.input_area').hide();
443 this.element.find("div.text_cell_render").show();
443 this.element.find("div.text_cell_render").show();
444 this.typeset();
444 this.typeset();
445 }
445 }
446 return cont;
446 return cont;
447 };
447 };
448
448
449 // TODO: RETURN IN THIS NAMESPACE!
449 IPython.TextCell = TextCell;
450 IPython.TextCell = TextCell;
450 IPython.MarkdownCell = MarkdownCell;
451 IPython.MarkdownCell = MarkdownCell;
451 IPython.RawCell = RawCell;
452 IPython.RawCell = RawCell;
452 IPython.HeadingCell = HeadingCell;
453 IPython.HeadingCell = HeadingCell;
453
454
454
455
455 return IPython;
456 return IPython;
456
457
457 }(IPython));
458 }(IPython));
458
459
@@ -1,362 +1,299 b''
1 {% extends "page.html" %}
1 {% extends "page.html" %}
2
2
3 {% block stylesheet %}
3 {% block stylesheet %}
4
4
5 {% if mathjax_url %}
5 {% if mathjax_url %}
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 {% endif %}
7 {% endif %}
8 <script type="text/javascript">
8 <script type="text/javascript">
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 // where it will be undefined, and should prompt a dialog later.
10 // where it will be undefined, and should prompt a dialog later.
11 window.mathjax_url = "{{mathjax_url}}";
11 window.mathjax_url = "{{mathjax_url}}";
12 </script>
12 </script>
13
13
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
14 <link rel="stylesheet" href="{{ static_url("components/bootstrap-tour/build/css/bootstrap-tour.min.css") }}" type="text/css" />
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
16
16
17 {{super()}}
17 {{super()}}
18
18
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
19 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
20
20
21 {% endblock %}
21 {% endblock %}
22
22
23 {% block params %}
23 {% block params %}
24
24
25 data-project="{{project}}"
25 data-project="{{project}}"
26 data-base-url="{{base_url}}"
26 data-base-url="{{base_url}}"
27 data-notebook-name="{{notebook_name}}"
27 data-notebook-name="{{notebook_name}}"
28 data-notebook-path="{{notebook_path}}"
28 data-notebook-path="{{notebook_path}}"
29 class="notebook_app"
29 class="notebook_app"
30
30
31 {% endblock %}
31 {% endblock %}
32
32
33
33
34 {% block header %}
34 {% block header %}
35
35
36 <span id="save_widget" class="nav pull-left">
36 <span id="save_widget" class="nav pull-left">
37 <span id="notebook_name"></span>
37 <span id="notebook_name"></span>
38 <span id="checkpoint_status"></span>
38 <span id="checkpoint_status"></span>
39 <span id="autosave_status"></span>
39 <span id="autosave_status"></span>
40 </span>
40 </span>
41
41
42 {% endblock %}
42 {% endblock %}
43
43
44
44
45 {% block site %}
45 {% block site %}
46
46
47 <div id="menubar-container" class="container">
47 <div id="menubar-container" class="container">
48 <div id="menubar">
48 <div id="menubar">
49 <div id="menus" class="navbar navbar-default" role="navigation">
49 <div id="menus" class="navbar navbar-default" role="navigation">
50 <div class="container-fluid">
50 <div class="container-fluid">
51 <ul class="nav navbar-nav">
51 <ul class="nav navbar-nav">
52 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
52 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
53 <ul id="file_menu" class="dropdown-menu">
53 <ul id="file_menu" class="dropdown-menu">
54 <li id="new_notebook"
54 <li id="new_notebook"
55 title="Make a new notebook (Opens a new window)">
55 title="Make a new notebook (Opens a new window)">
56 <a href="#">New</a></li>
56 <a href="#">New</a></li>
57 <li id="open_notebook"
57 <li id="open_notebook"
58 title="Opens a new window with the Dashboard view">
58 title="Opens a new window with the Dashboard view">
59 <a href="#">Open...</a></li>
59 <a href="#">Open...</a></li>
60 <!-- <hr/> -->
60 <!-- <hr/> -->
61 <li class="divider"></li>
61 <li class="divider"></li>
62 <li id="copy_notebook"
62 <li id="copy_notebook"
63 title="Open a copy of this notebook's contents and start a new kernel">
63 title="Open a copy of this notebook's contents and start a new kernel">
64 <a href="#">Make a Copy...</a></li>
64 <a href="#">Make a Copy...</a></li>
65 <li id="rename_notebook"><a href="#">Rename...</a></li>
65 <li id="rename_notebook"><a href="#">Rename...</a></li>
66 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
66 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
67 <!-- <hr/> -->
67 <!-- <hr/> -->
68 <li class="divider"></li>
68 <li class="divider"></li>
69 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
69 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
70 <ul class="dropdown-menu">
70 <ul class="dropdown-menu">
71 <li><a href="#"></a></li>
71 <li><a href="#"></a></li>
72 <li><a href="#"></a></li>
72 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
73 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
74 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
75 <li><a href="#"></a></li>
76 </ul>
76 </ul>
77 </li>
77 </li>
78 <li class="divider"></li>
78 <li class="divider"></li>
79 <li id="print_preview"><a href="#">Print Preview</a></li>
79 <li id="print_preview"><a href="#">Print Preview</a></li>
80 <li class="dropdown-submenu"><a href="#">Download as</a>
80 <li class="dropdown-submenu"><a href="#">Download as</a>
81 <ul class="dropdown-menu">
81 <ul class="dropdown-menu">
82 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
82 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
83 <li id="download_py"><a href="#">Python (.py)</a></li>
83 <li id="download_py"><a href="#">Python (.py)</a></li>
84 <li id="download_html"><a href="#">HTML (.html)</a></li>
84 <li id="download_html"><a href="#">HTML (.html)</a></li>
85 <li id="download_rst"><a href="#">reST (.rst)</a></li>
85 <li id="download_rst"><a href="#">reST (.rst)</a></li>
86 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
86 <li id="download_pdf"><a href="#">PDF (.pdf)</a></li>
87 </ul>
87 </ul>
88 </li>
88 </li>
89 <li class="divider"></li>
89 <li class="divider"></li>
90 <li id="trust_notebook"
90 <li id="trust_notebook"
91 title="Trust the output of this notebook">
91 title="Trust the output of this notebook">
92 <a href="#" >Trust Notebook</a></li>
92 <a href="#" >Trust Notebook</a></li>
93 <li class="divider"></li>
93 <li class="divider"></li>
94 <li id="kill_and_exit"
94 <li id="kill_and_exit"
95 title="Shutdown this notebook's kernel, and close this window">
95 title="Shutdown this notebook's kernel, and close this window">
96 <a href="#" >Close and halt</a></li>
96 <a href="#" >Close and halt</a></li>
97 </ul>
97 </ul>
98 </li>
98 </li>
99 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
99 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
100 <ul id="edit_menu" class="dropdown-menu">
100 <ul id="edit_menu" class="dropdown-menu">
101 <li id="cut_cell"><a href="#">Cut Cell</a></li>
101 <li id="cut_cell"><a href="#">Cut Cell</a></li>
102 <li id="copy_cell"><a href="#">Copy Cell</a></li>
102 <li id="copy_cell"><a href="#">Copy Cell</a></li>
103 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
103 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
104 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
104 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
105 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
105 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
106 <li id="delete_cell"><a href="#">Delete Cell</a></li>
106 <li id="delete_cell"><a href="#">Delete Cell</a></li>
107 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
107 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
108 <li class="divider"></li>
108 <li class="divider"></li>
109 <li id="split_cell"><a href="#">Split Cell</a></li>
109 <li id="split_cell"><a href="#">Split Cell</a></li>
110 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
110 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
111 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
111 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
112 <li class="divider"></li>
112 <li class="divider"></li>
113 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
113 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
114 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
114 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
115 <li class="divider"></li>
115 <li class="divider"></li>
116 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
116 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
117 </ul>
117 </ul>
118 </li>
118 </li>
119 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
119 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
120 <ul id="view_menu" class="dropdown-menu">
120 <ul id="view_menu" class="dropdown-menu">
121 <li id="toggle_header"
121 <li id="toggle_header"
122 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
122 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
123 <a href="#">Toggle Header</a></li>
123 <a href="#">Toggle Header</a></li>
124 <li id="toggle_toolbar"
124 <li id="toggle_toolbar"
125 title="Show/Hide the action icons (below menu bar)">
125 title="Show/Hide the action icons (below menu bar)">
126 <a href="#">Toggle Toolbar</a></li>
126 <a href="#">Toggle Toolbar</a></li>
127 </ul>
127 </ul>
128 </li>
128 </li>
129 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
129 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
130 <ul id="insert_menu" class="dropdown-menu">
130 <ul id="insert_menu" class="dropdown-menu">
131 <li id="insert_cell_above"
131 <li id="insert_cell_above"
132 title="Insert an empty Code cell above the currently active cell">
132 title="Insert an empty Code cell above the currently active cell">
133 <a href="#">Insert Cell Above</a></li>
133 <a href="#">Insert Cell Above</a></li>
134 <li id="insert_cell_below"
134 <li id="insert_cell_below"
135 title="Insert an empty Code cell below the currently active cell">
135 title="Insert an empty Code cell below the currently active cell">
136 <a href="#">Insert Cell Below</a></li>
136 <a href="#">Insert Cell Below</a></li>
137 </ul>
137 </ul>
138 </li>
138 </li>
139 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
139 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
140 <ul id="cell_menu" class="dropdown-menu">
140 <ul id="cell_menu" class="dropdown-menu">
141 <li id="run_cell" title="Run this cell, and move cursor to the next one">
141 <li id="run_cell" title="Run this cell, and move cursor to the next one">
142 <a href="#">Run</a></li>
142 <a href="#">Run</a></li>
143 <li id="run_cell_select_below" title="Run this cell, select below">
143 <li id="run_cell_select_below" title="Run this cell, select below">
144 <a href="#">Run and Select Below</a></li>
144 <a href="#">Run and Select Below</a></li>
145 <li id="run_cell_insert_below" title="Run this cell, insert below">
145 <li id="run_cell_insert_below" title="Run this cell, insert below">
146 <a href="#">Run and Insert Below</a></li>
146 <a href="#">Run and Insert Below</a></li>
147 <li id="run_all_cells" title="Run all cells in the notebook">
147 <li id="run_all_cells" title="Run all cells in the notebook">
148 <a href="#">Run All</a></li>
148 <a href="#">Run All</a></li>
149 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
149 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
150 <a href="#">Run All Above</a></li>
150 <a href="#">Run All Above</a></li>
151 <li id="run_all_cells_below" title="Run this cell and all cells below it">
151 <li id="run_all_cells_below" title="Run this cell and all cells below it">
152 <a href="#">Run All Below</a></li>
152 <a href="#">Run All Below</a></li>
153 <li class="divider"></li>
153 <li class="divider"></li>
154 <li id="change_cell_type" class="dropdown-submenu"
154 <li id="change_cell_type" class="dropdown-submenu"
155 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
155 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
156 <a href="#">Cell Type</a>
156 <a href="#">Cell Type</a>
157 <ul class="dropdown-menu">
157 <ul class="dropdown-menu">
158 <li id="to_code"
158 <li id="to_code"
159 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
159 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
160 <a href="#">Code</a></li>
160 <a href="#">Code</a></li>
161 <li id="to_markdown"
161 <li id="to_markdown"
162 title="Contents will be rendered as HTML and serve as explanatory text">
162 title="Contents will be rendered as HTML and serve as explanatory text">
163 <a href="#">Markdown</a></li>
163 <a href="#">Markdown</a></li>
164 <li id="to_raw"
164 <li id="to_raw"
165 title="Contents will pass through nbconvert unmodified">
165 title="Contents will pass through nbconvert unmodified">
166 <a href="#">Raw NBConvert</a></li>
166 <a href="#">Raw NBConvert</a></li>
167 <li id="to_heading1"><a href="#">Heading 1</a></li>
167 <li id="to_heading1"><a href="#">Heading 1</a></li>
168 <li id="to_heading2"><a href="#">Heading 2</a></li>
168 <li id="to_heading2"><a href="#">Heading 2</a></li>
169 <li id="to_heading3"><a href="#">Heading 3</a></li>
169 <li id="to_heading3"><a href="#">Heading 3</a></li>
170 <li id="to_heading4"><a href="#">Heading 4</a></li>
170 <li id="to_heading4"><a href="#">Heading 4</a></li>
171 <li id="to_heading5"><a href="#">Heading 5</a></li>
171 <li id="to_heading5"><a href="#">Heading 5</a></li>
172 <li id="to_heading6"><a href="#">Heading 6</a></li>
172 <li id="to_heading6"><a href="#">Heading 6</a></li>
173 </ul>
173 </ul>
174 </li>
174 </li>
175 <li class="divider"></li>
175 <li class="divider"></li>
176 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
176 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
177 <ul class="dropdown-menu">
177 <ul class="dropdown-menu">
178 <li id="toggle_current_output"
178 <li id="toggle_current_output"
179 title="Hide/Show the output of the current cell">
179 title="Hide/Show the output of the current cell">
180 <a href="#">Toggle</a>
180 <a href="#">Toggle</a>
181 </li>
181 </li>
182 <li id="toggle_current_output_scroll"
182 <li id="toggle_current_output_scroll"
183 title="Scroll the output of the current cell">
183 title="Scroll the output of the current cell">
184 <a href="#">Toggle Scrolling</a>
184 <a href="#">Toggle Scrolling</a>
185 </li>
185 </li>
186 <li id="clear_current_output"
186 <li id="clear_current_output"
187 title="Clear the output of the current cell">
187 title="Clear the output of the current cell">
188 <a href="#">Clear</a>
188 <a href="#">Clear</a>
189 </li>
189 </li>
190 </ul>
190 </ul>
191 </li>
191 </li>
192 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
192 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
193 <ul class="dropdown-menu">
193 <ul class="dropdown-menu">
194 <li id="toggle_all_output"
194 <li id="toggle_all_output"
195 title="Hide/Show the output of all cells">
195 title="Hide/Show the output of all cells">
196 <a href="#">Toggle</a>
196 <a href="#">Toggle</a>
197 </li>
197 </li>
198 <li id="toggle_all_output_scroll"
198 <li id="toggle_all_output_scroll"
199 title="Scroll the output of all cells">
199 title="Scroll the output of all cells">
200 <a href="#">Toggle Scrolling</a>
200 <a href="#">Toggle Scrolling</a>
201 </li>
201 </li>
202 <li id="clear_all_output"
202 <li id="clear_all_output"
203 title="Clear the output of all cells">
203 title="Clear the output of all cells">
204 <a href="#">Clear</a>
204 <a href="#">Clear</a>
205 </li>
205 </li>
206 </ul>
206 </ul>
207 </li>
207 </li>
208 </ul>
208 </ul>
209 </li>
209 </li>
210 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
210 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
211 <ul id="kernel_menu" class="dropdown-menu">
211 <ul id="kernel_menu" class="dropdown-menu">
212 <li id="int_kernel"
212 <li id="int_kernel"
213 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
213 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
214 <a href="#">Interrupt</a></li>
214 <a href="#">Interrupt</a></li>
215 <li id="restart_kernel"
215 <li id="restart_kernel"
216 title="Restart the Kernel">
216 title="Restart the Kernel">
217 <a href="#">Restart</a></li>
217 <a href="#">Restart</a></li>
218 </ul>
218 </ul>
219 </li>
219 </li>
220 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
220 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
221 <ul id="help_menu" class="dropdown-menu">
221 <ul id="help_menu" class="dropdown-menu">
222 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
222 <li id="notebook_tour" title="A quick tour of the notebook user interface"><a href="#">User Interface Tour</a></li>
223 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
223 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
224 <li class="divider"></li>
224 <li class="divider"></li>
225 {% set
225 {% set
226 sections = (
226 sections = (
227 (
227 (
228 ("http://ipython.org/documentation.html","IPython Help",True),
228 ("http://ipython.org/documentation.html","IPython Help",True),
229 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
229 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/2.x/examples/Index.ipynb", "Notebook Help", True),
230 ),(
230 ),(
231 ("http://docs.python.org","Python",True),
231 ("http://docs.python.org","Python",True),
232 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
232 ("http://help.github.com/articles/github-flavored-markdown","Markdown",True),
233 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
233 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
234 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
234 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
235 ("http://matplotlib.org/contents.html","Matplotlib",True),
235 ("http://matplotlib.org/contents.html","Matplotlib",True),
236 ("http://docs.sympy.org/latest/index.html","SymPy",True),
236 ("http://docs.sympy.org/latest/index.html","SymPy",True),
237 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
237 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
238 )
238 )
239 )
239 )
240 %}
240 %}
241
241
242 {% for helplinks in sections %}
242 {% for helplinks in sections %}
243 {% for link in helplinks %}
243 {% for link in helplinks %}
244 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
244 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
245 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
245 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
246 {{link[1]}}
246 {{link[1]}}
247 </a></li>
247 </a></li>
248 {% endfor %}
248 {% endfor %}
249 {% if not loop.last %}
249 {% if not loop.last %}
250 <li class="divider"></li>
250 <li class="divider"></li>
251 {% endif %}
251 {% endif %}
252 {% endfor %}
252 {% endfor %}
253 </li>
253 </li>
254 </ul>
254 </ul>
255 </li>
255 </li>
256 </ul>
256 </ul>
257 <ul class="nav navbar-nav navbar-right">
257 <ul class="nav navbar-nav navbar-right">
258 <div id="kernel_indicator">
258 <div id="kernel_indicator">
259 <i id="kernel_indicator_icon"></i>
259 <i id="kernel_indicator_icon"></i>
260 </div>
260 </div>
261 <div id="modal_indicator">
261 <div id="modal_indicator">
262 <i id="modal_indicator_icon"></i>
262 <i id="modal_indicator_icon"></i>
263 </div>
263 </div>
264 <div id="notification_area"></div>
264 <div id="notification_area"></div>
265 </ul>
265 </ul>
266 </div>
266 </div>
267 </div>
267 </div>
268 </div>
268 </div>
269 <div id="maintoolbar" class="navbar">
269 <div id="maintoolbar" class="navbar">
270 <div class="toolbar-inner navbar-inner navbar-nobg">
270 <div class="toolbar-inner navbar-inner navbar-nobg">
271 <div id="maintoolbar-container" class="container"></div>
271 <div id="maintoolbar-container" class="container"></div>
272 </div>
272 </div>
273 </div>
273 </div>
274 </div>
274 </div>
275
275
276 <div id="ipython-main-app">
276 <div id="ipython-main-app">
277
277
278 <div id="notebook_panel">
278 <div id="notebook_panel">
279 <div id="notebook"></div>
279 <div id="notebook"></div>
280 <div id="pager_splitter"></div>
280 <div id="pager_splitter"></div>
281 <div id="pager">
281 <div id="pager">
282 <div id='pager_button_area'>
282 <div id='pager_button_area'>
283 </div>
283 </div>
284 <div id="pager-container" class="container"></div>
284 <div id="pager-container" class="container"></div>
285 </div>
285 </div>
286 </div>
286 </div>
287
287
288 </div>
288 </div>
289 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
289 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
290
290
291
291
292 {% endblock %}
292 {% endblock %}
293
293
294
294
295 {% block script %}
295 {% block script %}
296
296
297 {{super()}}
297 <script src="{{ static_url("notebook/js/main.js") }}" charset="utf-8"></script>
298
299 <script src="{{ static_url("components/google-caja/html-css-sanitizer-minified.js") }}" charset="utf-8"></script>
300 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
301 <script type="text/javascript">
302 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
303 </script>
304 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
305 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
306 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
307 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
308 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
309 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
310 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
311 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
312 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
313 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
314 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
315 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
316 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
317 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
318 <script src="{{ static_url("notebook/js/codemirror-ipythongfm.js") }}" charset="utf-8"></script>
319
320 <script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
321
322 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
323
324 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
325 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
326 <script src="{{ static_url("base/js/keyboard.js") }}" type="text/javascript" charset="utf-8"></script>
327 <script src="{{ static_url("base/js/security.js") }}" type="text/javascript" charset="utf-8"></script>
328 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
329 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
330 <script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
331 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
332 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
333 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
334 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
335 <script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
336 <script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
337 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
338 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
339 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
340 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
341 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
342 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
343 <script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
344 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
345 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
346 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
347 <script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
348 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
349 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
350 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
351 <script src="{{ static_url("notebook/js/tour.js") }}" type="text/javascript" charset="utf-8"></script>
352
353 <script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
354 <script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
355
356 <script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
357
358 <script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
359 <script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
360 <script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
361
298
362 {% endblock %}
299 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now