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