##// END OF EJS Templates
Merge pull request #3393 from minrk/bootstrap...
Brian E. Granger -
r10974:20c70fc4 merge
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -0,0 +1,75 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 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 // Utility for modal dialogs with bootstrap
10 //============================================================================
11
12 IPython.namespace('IPython.dialog');
13
14 IPython.dialog = (function (IPython) {
15
16 var modal = function (options) {
17 var dialog = $("<div/>").addClass("modal").attr("role", "dialog");
18 dialog.append(
19 $("<div/>")
20 .addClass("modal-header")
21 .append($("<button>")
22 .addClass("close")
23 .attr("data-dismiss", "modal")
24 .html("&times;")
25 ).append(
26 $("<h3/>").text(options.title || "")
27 )
28 ).append(
29 $("<div/>").addClass("modal-body").append(
30 options.body || $("<p/>")
31 )
32 );
33
34 var footer = $("<div/>").addClass("modal-footer");
35
36 for (var label in options.buttons) {
37 var btn_opts = options.buttons[label];
38 var button = $("<button/>")
39 .addClass("btn")
40 .attr("data-dismiss", "modal")
41 .text(label);
42 if (btn_opts.click) {
43 button.click($.proxy(btn_opts.click, dialog));
44 }
45 if (btn_opts.class) {
46 button.addClass(btn_opts.class);
47 }
48 footer.append(button);
49 }
50 dialog.append(footer);
51 // hook up on-open event
52 dialog.on("shown", function() {
53 setTimeout(function() {
54 footer.find("button").last().focus();
55 if (options.open) {
56 $.proxy(options.open, dialog)();
57 }
58 }, 0);
59 });
60
61 // destroy dialog on hide, unless explicitly asked not to
62 if (options.destroy == undefined || options.destroy) {
63 dialog.on("hidden", function () {
64 dialog.remove();
65 });
66 }
67
68 return dialog.modal(options);
69 }
70
71 return {
72 modal : modal,
73 };
74
75 }(IPython));
@@ -1,22 +1,21 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // On document ready
9 // On document ready
10 //============================================================================
10 //============================================================================
11
11
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14
14
15 IPython.page = new IPython.Page();
15 IPython.page = new IPython.Page();
16 $('input#login_submit').button();
16 $('button#login_submit').addClass("btn");
17 $('#ipython-main-app').addClass('border-box-sizing ui-widget');
18 IPython.page.show();
17 IPython.page.show();
19 $('input#password_input').focus();
18 $('input#password_input').focus();
20
19
21 });
20 });
22
21
@@ -1,46 +1,45 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Login button
9 // Login button
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var LoginWidget = function (selector, options) {
14 var LoginWidget = function (selector, options) {
15 var options = options || {};
15 var options = options || {};
16 this.base_url = options.baseProjectUrl || $('body').data('baseProjectUrl') ;
16 this.base_url = options.baseProjectUrl || $('body').data('baseProjectUrl') ;
17 this.selector = selector;
17 this.selector = selector;
18 if (this.selector !== undefined) {
18 if (this.selector !== undefined) {
19 this.element = $(selector);
19 this.element = $(selector);
20 this.style();
20 this.style();
21 this.bind_events();
21 this.bind_events();
22 }
22 }
23 };
23 };
24
24
25 LoginWidget.prototype.style = function () {
25 LoginWidget.prototype.style = function () {
26 this.element.find('button#logout').button();
26 this.element.find("button").addClass("btn btn-small");
27 this.element.find('button#login').button();
28 };
27 };
29
28
30
29
31 LoginWidget.prototype.bind_events = function () {
30 LoginWidget.prototype.bind_events = function () {
32 var that = this;
31 var that = this;
33 this.element.find("button#logout").click(function () {
32 this.element.find("button#logout").click(function () {
34 window.location = that.base_url+"logout";
33 window.location = that.base_url+"logout";
35 });
34 });
36 this.element.find("button#login").click(function () {
35 this.element.find("button#login").click(function () {
37 window.location = that.base_url+"login";
36 window.location = that.base_url+"login";
38 });
37 });
39 };
38 };
40
39
41 // Set module variables
40 // Set module variables
42 IPython.LoginWidget = LoginWidget;
41 IPython.LoginWidget = LoginWidget;
43
42
44 return IPython;
43 return IPython;
45
44
46 }(IPython));
45 }(IPython));
@@ -1,20 +1,20 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // On document ready
9 // On document ready
10 //============================================================================
10 //============================================================================
11
11
12
12
13 $(document).ready(function () {
13 $(document).ready(function () {
14
14
15 IPython.page = new IPython.Page();
15 IPython.page = new IPython.Page();
16 $('#ipython-main-app').addClass('border-box-sizing ui-widget');
16 $('#ipython-main-app').addClass('border-box-sizing');
17 IPython.page.show();
17 IPython.page.show();
18
18
19 });
19 });
20
20
@@ -1,1 +1,6 b''
1 // Custom styles for login.html No newline at end of file
1 // Custom styles for login.html
2 .center-nav {
3 display: inline-block;
4 // pull the lower margin back
5 margin-bottom: -4px;
6 } No newline at end of file
@@ -1,59 +1,55 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Global header/site setup.
9 // Global header/site setup.
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var Page = function () {
14 var Page = function () {
15 this.style();
15 this.style();
16 this.bind_events();
16 this.bind_events();
17 };
17 };
18
18
19 Page.prototype.style = function () {
19 Page.prototype.style = function () {
20 $('div#header').addClass('border-box-sizing').
20 $('div#header').addClass('border-box-sizing');
21 addClass('ui-widget-content').
21 $('div#site').addClass('border-box-sizing');
22 css('border-top-style','none').
23 css('border-left-style','none').
24 css('border-right-style','none');
25 $('div#site').addClass('border-box-sizing')
26 };
22 };
27
23
28
24
29 Page.prototype.bind_events = function () {
25 Page.prototype.bind_events = function () {
30 };
26 };
31
27
32
28
33 Page.prototype.show = function () {
29 Page.prototype.show = function () {
34 // The header and site divs start out hidden to prevent FLOUC.
30 // The header and site divs start out hidden to prevent FLOUC.
35 // Main scripts should call this method after styling everything.
31 // Main scripts should call this method after styling everything.
36 this.show_header();
32 this.show_header();
37 this.show_site();
33 this.show_site();
38 };
34 };
39
35
40
36
41 Page.prototype.show_header = function () {
37 Page.prototype.show_header = function () {
42 // The header and site divs start out hidden to prevent FLOUC.
38 // The header and site divs start out hidden to prevent FLOUC.
43 // Main scripts should call this method after styling everything.
39 // Main scripts should call this method after styling everything.
44 $('div#header').css('display','block');
40 $('div#header').css('display','block');
45 };
41 };
46
42
47
43
48 Page.prototype.show_site = function () {
44 Page.prototype.show_site = function () {
49 // The header and site divs start out hidden to prevent FLOUC.
45 // The header and site divs start out hidden to prevent FLOUC.
50 // Main scripts should call this method after styling everything.
46 // Main scripts should call this method after styling everything.
51 $('div#site').css('display','block');
47 $('div#site').css('display','block');
52 };
48 };
53
49
54
50
55 IPython.Page = Page;
51 IPython.Page = Page;
56
52
57 return IPython;
53 return IPython;
58
54
59 }(IPython));
55 }(IPython));
@@ -1,70 +1,73 b''
1 /**
1 /**
2 * Primary styles
2 * Primary styles
3 *
3 *
4 * Author: IPython Development Team
4 * Author: IPython Development Team
5 */
5 */
6
6
7
7
8 body {
8 body {
9 background-color: white;
9 background-color: white;
10 /* This makes sure that the body covers the entire window and needs to
10 /* This makes sure that the body covers the entire window and needs to
11 be in a different element than the display: box in wrapper below */
11 be in a different element than the display: box in wrapper below */
12 position: absolute;
12 position: absolute;
13 left: 0px;
13 left: 0px;
14 right: 0px;
14 right: 0px;
15 top: 0px;
15 top: 0px;
16 bottom: 0px;
16 bottom: 0px;
17 overflow: visible;
17 overflow: visible;
18 }
18 }
19
19
20
21 div#header {
20 div#header {
22 /* Initially hidden to prevent FLOUC */
21 /* Initially hidden to prevent FLOUC */
23 display: none;
22 display: none;
24 position: relative;
25 height: 40px;
26 padding: 5px;
27 margin: 0px;
28 width: 100%;
29 }
23 }
30
24
31 span#ipython_notebook {
25 #ipython_notebook {
32 position: absolute;
26 padding-left: 16px;
33 padding: 2px 2px 2px 5px;
34 }
27 }
35
28
36 span#ipython_notebook img {
29 #ipython_notebook img {
37 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
30 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
38 height: 24px;
31 height: 24px;
39 text-decoration:none;
32 text-decoration:none;
40 display: inline;
41 color: black;
33 color: black;
42 }
34 }
43
35
44 #site {
36 #site {
45 width: 100%;
37 width: 100%;
46 display: none;
38 display: none;
47 }
39 }
48
40
49 /* We set the fonts by hand here to override the values in the theme */
50 .ui-widget {
51 font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
52 }
53
54 .ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button {
55 font-family: "Lucinda Grande", "Lucinda Sans Unicode", Helvetica, Arial, Verdana, sans-serif;
56 }
57
58 /* Smaller buttons */
41 /* Smaller buttons */
59 .ui-button .ui-button-text {
42 .ui-button .ui-button-text {
60 padding: 0.2em 0.8em;
43 padding: 0.2em 0.8em;
61 font-size: 77%;
44 font-size: 77%;
62 }
45 }
63
46
64 input.ui-button {
47 input.ui-button {
65 padding: 0.3em 0.9em;
48 padding: 0.3em 0.9em;
66 }
49 }
50 .navbar span {
51 margin-top: 3px;
52 }
67
53
68 span#login_widget {
54 span#login_widget {
69 float: right;
55 float: right;
70 }
56 }
57
58 .nav-header {
59 text-transform: none;
60 }
61
62 .navbar-nobg {
63 background-color: transparent;
64 background-image: none;
65 }
66
67 #header > span {
68 margin-top: 10px;
69 }
70
71 .modal-body {
72 max-height: 500px;
73 }
@@ -1,4 +1,6 b''
1 @import "../base/less/variables.less";
1 @import "../base/less/variables.less";
2 @import "../base/less/mixins.less";
2 @import "../base/less/mixins.less";
3 @import "../base/less/flexbox.less";
3 @import "../base/less/flexbox.less";
4 @import "../base/less/page.less";
4 @import "../base/less/page.less";
5 @import "../components/font-awesome/build/assets/font-awesome/less/font-awesome.less";
6 @FontAwesomePath: "../components/font-awesome/build/assets/font-awesome/font";
@@ -1,10 +1,11 b''
1 // Our customizations to bootstrap go here.
1 // Our customizations to bootstrap go here.
2
2
3 @textColor: @black;
3 @textColor: @black;
4 @baseFontSize: 13px;
4 @baseFontSize: 13px;
5 @baseLineHeight: 1.231;
5 @monoFontFamily: monospace; // to allow user to customize their fonts
6 @monoFontFamily: monospace; // to allow user to customize their fonts
6 @navbarHeight: 36px;
7
7
8 // Our own global variables for all pages go here
8 // Our own global variables for all pages go here
9
9
10 @corner_radius: 4px; No newline at end of file
10 @corner_radius: 4px;
11 @code_line_height: 1.231em;
@@ -1,9 +1,8 b''
1 /*This file contains any manual css for this page that needs to override the global styles.
1 /*This file contains any manual css for this page that needs to override the global styles.
2 This is only required when different pages style the same element differently. This is just
2 This is only required when different pages style the same element differently. This is just
3 a hack to deal with our current css styles and no new styling should be added in this file.*/
3 a hack to deal with our current css styles and no new styling should be added in this file.*/
4
4
5 #ipython-main-app {
5 #ipython-main-app {
6 width: 100%;
7 position: relative;
6 position: relative;
8 font-size: 110%;
7 font-size: 110%;
9 } No newline at end of file
8 }
@@ -1,94 +1,90 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2012 The IPython Development Team
2 // Copyright (C) 2012 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CellToolbar Default
9 // CellToolbar Default
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * Example Use for the CellToolbar library
13 * Example Use for the CellToolbar library
14 */
14 */
15 // IIFE without asignement, we don't modifiy the IPython namespace
15 // IIFE without asignement, we don't modifiy the IPython namespace
16 (function (IPython) {
16 (function (IPython) {
17 "use strict";
17 "use strict";
18
18
19 var CellToolbar = IPython.CellToolbar;
19 var CellToolbar = IPython.CellToolbar;
20
20
21 var raw_edit = function(cell){
21 var raw_edit = function(cell){
22
22
23 var md = cell.metadata
23 var md = cell.metadata
24 var error_div = $('<div/>').css('color','red')
24 var error_div = $('<div/>').css('color','red')
25
25
26 var textarea = $('<textarea/>')
26 var textarea = $('<textarea/>')
27 .attr('rows','13')
27 .attr('rows','13')
28 .attr('cols','75')
28 .attr('cols','75')
29 .attr('name','metadata')
29 .attr('name','metadata')
30 .text(JSON.stringify(md, null,4)||'');
30 .text(JSON.stringify(md, null,4)||'');
31 var dialogform = $('<div/>').attr('title','Edit the metadata')
31 var dialogform = $('<div/>').attr('title','Edit the metadata')
32 .append(
32 .append(
33 $('<form/>').append(
33 $('<form/>').append(
34 $('<fieldset/>').append(
34 $('<fieldset/>').append(
35 $('<label/>')
35 $('<label/>')
36 .attr('for','metadata')
36 .attr('for','metadata')
37 .text("Manually edit the JSON below to manipulate the metadata for this cell. This assumes you know what you are doing and won't complain if it breaks your notebook. We also recommend putting your metadata attributes in an appropriately named sub-structure, so they don't conflict with those of others.")
37 .text("Manually edit the JSON below to manipulate the metadata for this cell. This assumes you know what you are doing and won't complain if it breaks your notebook. We also recommend putting your metadata attributes in an appropriately named sub-structure, so they don't conflict with those of others.")
38 )
38 )
39 .append(error_div)
39 .append(error_div)
40 .append($('<br/>'))
40 .append($('<br/>'))
41 .append(
41 .append(
42 textarea
42 textarea
43 )
43 )
44 )
44 )
45 );
45 );
46 var editor = CodeMirror.fromTextArea(textarea[0], {
46 var editor = CodeMirror.fromTextArea(textarea[0], {
47 lineNumbers: true,
47 lineNumbers: true,
48 matchBrackets: true,
48 matchBrackets: true,
49 });
49 });
50 $(dialogform).dialog({
50 IPython.dialog.modal({
51 autoOpen: true,
51 title: "Edit Cell Metadata",
52 height: 300,
52 body: dialogform,
53 width: 650,
54 modal: true,
55 buttons: {
53 buttons: {
56 "Ok": function() {
54 "OK": { class : "btn-primary",
55 click: function() {
57 //validate json and set it
56 //validate json and set it
58 try {
57 try {
59 var json = JSON.parse(editor.getValue());
58 var json = JSON.parse(editor.getValue());
60 cell.metadata = json;
59 cell.metadata = json;
61 $( this ).dialog( "close" );
60 } catch(e) {
62 }
63 catch(e)
64 {
65 error_div.text('Warning, invalid json, not saved');
61 error_div.text('Warning, invalid json, not saved');
62 return false;
66 }
63 }
67 },
64 }},
68 Cancel: function() {
65 Cancel: {}
69 $( this ).dialog( "close" );
70 }
71 },
72 close: function() {
73 //cleanup on close
74 $(this).remove();
75 }
66 }
76 });
67 });
77 editor.refresh();
68 editor.refresh();
78 }
69 }
79
70
80 var add_raw_edit_button = function(div, cell) {
71 var add_raw_edit_button = function(div, cell) {
81 var button_container = div
72 var button_container = div;
82 var button = $('<div/>').button({label:'Raw Edit'})
73 var button = $('<button/>')
83 .click(function(){raw_edit(cell); return false;})
74 .addClass("btn btn-mini")
75 .text("Raw Edit")
76 .click( function () {
77 raw_edit(cell);
78 return false;
79 });
84 button_container.append(button);
80 button_container.append(button);
85 }
81 }
86
82
87 CellToolbar.register_callback('default.rawedit',add_raw_edit_button);
83 CellToolbar.register_callback('default.rawedit',add_raw_edit_button);
88 var example_preset = []
84 var example_preset = []
89 example_preset.push('default.rawedit');
85 example_preset.push('default.rawedit');
90
86
91 CellToolbar.register_preset('Default',example_preset);
87 CellToolbar.register_preset('Default',example_preset);
92 console.log('Default extension for metadata editing loaded.');
88 console.log('Default extension for metadata editing loaded.');
93
89
94 }(IPython));
90 }(IPython));
@@ -1,439 +1,440 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // CodeCell
9 // CodeCell
10 //============================================================================
10 //============================================================================
11 /**
11 /**
12 * An extendable module that provide base functionnality to create cell for notebook.
12 * An extendable module that provide base functionnality to create cell for notebook.
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule CodeCell
15 * @submodule CodeCell
16 */
16 */
17
17
18
18
19 /* local util for codemirror */
19 /* local util for codemirror */
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}
20 var posEq = function(a, b) {return a.line == b.line && a.ch == b.ch;}
21
21
22 /**
22 /**
23 *
23 *
24 * function to delete until previous non blanking space character
24 * function to delete until previous non blanking space character
25 * or first multiple of 4 tabstop.
25 * or first multiple of 4 tabstop.
26 * @private
26 * @private
27 */
27 */
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
28 CodeMirror.commands.delSpaceToPrevTabStop = function(cm){
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
29 var from = cm.getCursor(true), to = cm.getCursor(false), sel = !posEq(from, to);
30 if (!posEq(from, to)) {cm.replaceRange("", from, to); return}
30 if (!posEq(from, to)) {cm.replaceRange("", from, to); return}
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
31 var cur = cm.getCursor(), line = cm.getLine(cur.line);
32 var tabsize = cm.getOption('tabSize');
32 var tabsize = cm.getOption('tabSize');
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
33 var chToPrevTabStop = cur.ch-(Math.ceil(cur.ch/tabsize)-1)*tabsize;
34 var from = {ch:cur.ch-chToPrevTabStop,line:cur.line}
34 var from = {ch:cur.ch-chToPrevTabStop,line:cur.line}
35 var select = cm.getRange(from,cur)
35 var select = cm.getRange(from,cur)
36 if( select.match(/^\ +$/) != null){
36 if( select.match(/^\ +$/) != null){
37 cm.replaceRange("",from,cur)
37 cm.replaceRange("",from,cur)
38 } else {
38 } else {
39 cm.deleteH(-1,"char")
39 cm.deleteH(-1,"char")
40 }
40 }
41 };
41 };
42
42
43
43
44 var IPython = (function (IPython) {
44 var IPython = (function (IPython) {
45 "use strict";
45 "use strict";
46
46
47 var utils = IPython.utils;
47 var utils = IPython.utils;
48 var key = IPython.utils.keycodes;
48 var key = IPython.utils.keycodes;
49 CodeMirror.modeURL = "/static/components/codemirror/mode/%N/%N.js";
49 CodeMirror.modeURL = "/static/components/codemirror/mode/%N/%N.js";
50
50
51 /**
51 /**
52 * A Cell conceived to write code.
52 * A Cell conceived to write code.
53 *
53 *
54 * The kernel doesn't have to be set at creation time, in that case
54 * The kernel doesn't have to be set at creation time, in that case
55 * it will be null and set_kernel has to be called later.
55 * it will be null and set_kernel has to be called later.
56 * @class CodeCell
56 * @class CodeCell
57 * @extends IPython.Cell
57 * @extends IPython.Cell
58 *
58 *
59 * @constructor
59 * @constructor
60 * @param {Object|null} kernel
60 * @param {Object|null} kernel
61 * @param {object|undefined} [options]
61 * @param {object|undefined} [options]
62 * @param [options.cm_config] {object} config to pass to CodeMirror
62 * @param [options.cm_config] {object} config to pass to CodeMirror
63 */
63 */
64 var CodeCell = function (kernel, options) {
64 var CodeCell = function (kernel, options) {
65 this.kernel = kernel || null;
65 this.kernel = kernel || null;
66 this.code_mirror = null;
66 this.code_mirror = null;
67 this.input_prompt_number = null;
67 this.input_prompt_number = null;
68 this.collapsed = false;
68 this.collapsed = false;
69 this.default_mode = 'ipython';
69 this.default_mode = 'ipython';
70 this.cell_type = "code";
70
71
71
72
72 var cm_overwrite_options = {
73 var cm_overwrite_options = {
73 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
74 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
74 };
75 };
75
76
76 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
77 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
77
78
78 IPython.Cell.apply(this,[options]);
79 IPython.Cell.apply(this,[options]);
79
80
80 var that = this;
81 var that = this;
81 this.element.focusout(
82 this.element.focusout(
82 function() { that.auto_highlight(); }
83 function() { that.auto_highlight(); }
83 );
84 );
84 };
85 };
85
86
86 CodeCell.options_default = {
87 CodeCell.options_default = {
87 cm_config : {
88 cm_config : {
88 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
89 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess",'Backspace':"delSpaceToPrevTabStop"},
89 mode: 'ipython',
90 mode: 'ipython',
90 theme: 'ipython',
91 theme: 'ipython',
91 matchBrackets: true
92 matchBrackets: true
92 }
93 }
93 };
94 };
94
95
95
96
96 CodeCell.prototype = new IPython.Cell();
97 CodeCell.prototype = new IPython.Cell();
97
98
98 /**
99 /**
99 * @method auto_highlight
100 * @method auto_highlight
100 */
101 */
101 CodeCell.prototype.auto_highlight = function () {
102 CodeCell.prototype.auto_highlight = function () {
102 this._auto_highlight(IPython.config.cell_magic_highlight)
103 this._auto_highlight(IPython.config.cell_magic_highlight)
103 };
104 };
104
105
105 /** @method create_element */
106 /** @method create_element */
106 CodeCell.prototype.create_element = function () {
107 CodeCell.prototype.create_element = function () {
107 IPython.Cell.prototype.create_element.apply(this, arguments);
108 IPython.Cell.prototype.create_element.apply(this, arguments);
108
109
109 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
110 var cell = $('<div></div>').addClass('cell border-box-sizing code_cell');
110 cell.attr('tabindex','2');
111 cell.attr('tabindex','2');
111
112
112 this.celltoolbar = new IPython.CellToolbar(this);
113 this.celltoolbar = new IPython.CellToolbar(this);
113
114
114 var input = $('<div></div>').addClass('input');
115 var input = $('<div></div>').addClass('input');
115 var vbox = $('<div/>').addClass('vbox box-flex1')
116 var vbox = $('<div/>').addClass('vbox box-flex1')
116 input.append($('<div/>').addClass('prompt input_prompt'));
117 input.append($('<div/>').addClass('prompt input_prompt'));
117 vbox.append(this.celltoolbar.element);
118 vbox.append(this.celltoolbar.element);
118 var input_area = $('<div/>').addClass('input_area');
119 var input_area = $('<div/>').addClass('input_area');
119 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
120 this.code_mirror = CodeMirror(input_area.get(0), this.cm_config);
120 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
121 $(this.code_mirror.getInputField()).attr("spellcheck", "false");
121 vbox.append(input_area);
122 vbox.append(input_area);
122 input.append(vbox);
123 input.append(vbox);
123 var output = $('<div></div>');
124 var output = $('<div></div>');
124 cell.append(input).append(output);
125 cell.append(input).append(output);
125 this.element = cell;
126 this.element = cell;
126 this.output_area = new IPython.OutputArea(output, true);
127 this.output_area = new IPython.OutputArea(output, true);
127
128
128 // construct a completer only if class exist
129 // construct a completer only if class exist
129 // otherwise no print view
130 // otherwise no print view
130 if (IPython.Completer !== undefined)
131 if (IPython.Completer !== undefined)
131 {
132 {
132 this.completer = new IPython.Completer(this);
133 this.completer = new IPython.Completer(this);
133 }
134 }
134 };
135 };
135
136
136 /**
137 /**
137 * This method gets called in CodeMirror's onKeyDown/onKeyPress
138 * This method gets called in CodeMirror's onKeyDown/onKeyPress
138 * handlers and is used to provide custom key handling. Its return
139 * handlers and is used to provide custom key handling. Its return
139 * value is used to determine if CodeMirror should ignore the event:
140 * value is used to determine if CodeMirror should ignore the event:
140 * true = ignore, false = don't ignore.
141 * true = ignore, false = don't ignore.
141 * @method handle_codemirror_keyevent
142 * @method handle_codemirror_keyevent
142 */
143 */
143 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
144 CodeCell.prototype.handle_codemirror_keyevent = function (editor, event) {
144
145
145 if (this.read_only){
146 if (this.read_only){
146 return false;
147 return false;
147 }
148 }
148
149
149 var that = this;
150 var that = this;
150 // whatever key is pressed, first, cancel the tooltip request before
151 // whatever key is pressed, first, cancel the tooltip request before
151 // they are sent, and remove tooltip if any, except for tab again
152 // they are sent, and remove tooltip if any, except for tab again
152 if (event.type === 'keydown' && event.which != key.TAB ) {
153 if (event.type === 'keydown' && event.which != key.TAB ) {
153 IPython.tooltip.remove_and_cancel_tooltip();
154 IPython.tooltip.remove_and_cancel_tooltip();
154 };
155 };
155
156
156 var cur = editor.getCursor();
157 var cur = editor.getCursor();
157 if (event.keyCode === key.ENTER){
158 if (event.keyCode === key.ENTER){
158 this.auto_highlight();
159 this.auto_highlight();
159 }
160 }
160
161
161 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
162 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
162 // Always ignore shift-enter in CodeMirror as we handle it.
163 // Always ignore shift-enter in CodeMirror as we handle it.
163 return true;
164 return true;
164 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
165 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
165 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
166 // triger on keypress (!) otherwise inconsistent event.which depending on plateform
166 // browser and keyboard layout !
167 // browser and keyboard layout !
167 // Pressing '(' , request tooltip, don't forget to reappend it
168 // Pressing '(' , request tooltip, don't forget to reappend it
168 IPython.tooltip.pending(that);
169 IPython.tooltip.pending(that);
169 } else if (event.which === key.UPARROW && event.type === 'keydown') {
170 } else if (event.which === key.UPARROW && event.type === 'keydown') {
170 // If we are not at the top, let CM handle the up arrow and
171 // If we are not at the top, let CM handle the up arrow and
171 // prevent the global keydown handler from handling it.
172 // prevent the global keydown handler from handling it.
172 if (!that.at_top()) {
173 if (!that.at_top()) {
173 event.stop();
174 event.stop();
174 return false;
175 return false;
175 } else {
176 } else {
176 return true;
177 return true;
177 };
178 };
178 } else if (event.which === key.ESC) {
179 } else if (event.which === key.ESC) {
179 IPython.tooltip.remove_and_cancel_tooltip(true);
180 IPython.tooltip.remove_and_cancel_tooltip(true);
180 return true;
181 return true;
181 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
182 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
182 // If we are not at the bottom, let CM handle the down arrow and
183 // If we are not at the bottom, let CM handle the down arrow and
183 // prevent the global keydown handler from handling it.
184 // prevent the global keydown handler from handling it.
184 if (!that.at_bottom()) {
185 if (!that.at_bottom()) {
185 event.stop();
186 event.stop();
186 return false;
187 return false;
187 } else {
188 } else {
188 return true;
189 return true;
189 };
190 };
190 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
191 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
191 if (editor.somethingSelected()){
192 if (editor.somethingSelected()){
192 var anchor = editor.getCursor("anchor");
193 var anchor = editor.getCursor("anchor");
193 var head = editor.getCursor("head");
194 var head = editor.getCursor("head");
194 if( anchor.line != head.line){
195 if( anchor.line != head.line){
195 return false;
196 return false;
196 }
197 }
197 }
198 }
198 IPython.tooltip.request(that);
199 IPython.tooltip.request(that);
199 event.stop();
200 event.stop();
200 return true;
201 return true;
201 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
202 } else if (event.keyCode === key.TAB && event.type == 'keydown') {
202 // Tab completion.
203 // Tab completion.
203 //Do not trim here because of tooltip
204 //Do not trim here because of tooltip
204 if (editor.somethingSelected()){return false}
205 if (editor.somethingSelected()){return false}
205 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
206 var pre_cursor = editor.getRange({line:cur.line,ch:0},cur);
206 if (pre_cursor.trim() === "") {
207 if (pre_cursor.trim() === "") {
207 // Don't autocomplete if the part of the line before the cursor
208 // Don't autocomplete if the part of the line before the cursor
208 // is empty. In this case, let CodeMirror handle indentation.
209 // is empty. In this case, let CodeMirror handle indentation.
209 return false;
210 return false;
210 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
211 } else if ((pre_cursor.substr(-1) === "("|| pre_cursor.substr(-1) === " ") && IPython.config.tooltip_on_tab ) {
211 IPython.tooltip.request(that);
212 IPython.tooltip.request(that);
212 // Prevent the event from bubbling up.
213 // Prevent the event from bubbling up.
213 event.stop();
214 event.stop();
214 // Prevent CodeMirror from handling the tab.
215 // Prevent CodeMirror from handling the tab.
215 return true;
216 return true;
216 } else {
217 } else {
217 event.stop();
218 event.stop();
218 this.completer.startCompletion();
219 this.completer.startCompletion();
219 return true;
220 return true;
220 };
221 };
221 } else {
222 } else {
222 // keypress/keyup also trigger on TAB press, and we don't want to
223 // keypress/keyup also trigger on TAB press, and we don't want to
223 // use those to disable tab completion.
224 // use those to disable tab completion.
224 return false;
225 return false;
225 };
226 };
226 return false;
227 return false;
227 };
228 };
228
229
229
230
230 // Kernel related calls.
231 // Kernel related calls.
231
232
232 CodeCell.prototype.set_kernel = function (kernel) {
233 CodeCell.prototype.set_kernel = function (kernel) {
233 this.kernel = kernel;
234 this.kernel = kernel;
234 }
235 }
235
236
236 /**
237 /**
237 * Execute current code cell to the kernel
238 * Execute current code cell to the kernel
238 * @method execute
239 * @method execute
239 */
240 */
240 CodeCell.prototype.execute = function () {
241 CodeCell.prototype.execute = function () {
241 this.output_area.clear_output(true, true, true);
242 this.output_area.clear_output(true, true, true);
242 this.set_input_prompt('*');
243 this.set_input_prompt('*');
243 this.element.addClass("running");
244 this.element.addClass("running");
244 var callbacks = {
245 var callbacks = {
245 'execute_reply': $.proxy(this._handle_execute_reply, this),
246 'execute_reply': $.proxy(this._handle_execute_reply, this),
246 'output': $.proxy(this.output_area.handle_output, this.output_area),
247 'output': $.proxy(this.output_area.handle_output, this.output_area),
247 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
248 'clear_output': $.proxy(this.output_area.handle_clear_output, this.output_area),
248 'set_next_input': $.proxy(this._handle_set_next_input, this),
249 'set_next_input': $.proxy(this._handle_set_next_input, this),
249 'input_request': $.proxy(this._handle_input_request, this)
250 'input_request': $.proxy(this._handle_input_request, this)
250 };
251 };
251 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
252 var msg_id = this.kernel.execute(this.get_text(), callbacks, {silent: false});
252 };
253 };
253
254
254 /**
255 /**
255 * @method _handle_execute_reply
256 * @method _handle_execute_reply
256 * @private
257 * @private
257 */
258 */
258 CodeCell.prototype._handle_execute_reply = function (content) {
259 CodeCell.prototype._handle_execute_reply = function (content) {
259 this.set_input_prompt(content.execution_count);
260 this.set_input_prompt(content.execution_count);
260 this.element.removeClass("running");
261 this.element.removeClass("running");
261 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
262 $([IPython.events]).trigger('set_dirty.Notebook', {value: true});
262 }
263 }
263
264
264 /**
265 /**
265 * @method _handle_set_next_input
266 * @method _handle_set_next_input
266 * @private
267 * @private
267 */
268 */
268 CodeCell.prototype._handle_set_next_input = function (text) {
269 CodeCell.prototype._handle_set_next_input = function (text) {
269 var data = {'cell': this, 'text': text}
270 var data = {'cell': this, 'text': text}
270 $([IPython.events]).trigger('set_next_input.Notebook', data);
271 $([IPython.events]).trigger('set_next_input.Notebook', data);
271 }
272 }
272
273
273 /**
274 /**
274 * @method _handle_input_request
275 * @method _handle_input_request
275 * @private
276 * @private
276 */
277 */
277 CodeCell.prototype._handle_input_request = function (content) {
278 CodeCell.prototype._handle_input_request = function (content) {
278 this.output_area.append_raw_input(content);
279 this.output_area.append_raw_input(content);
279 }
280 }
280
281
281
282
282 // Basic cell manipulation.
283 // Basic cell manipulation.
283
284
284 CodeCell.prototype.select = function () {
285 CodeCell.prototype.select = function () {
285 IPython.Cell.prototype.select.apply(this);
286 IPython.Cell.prototype.select.apply(this);
286 this.code_mirror.refresh();
287 this.code_mirror.refresh();
287 this.code_mirror.focus();
288 this.code_mirror.focus();
288 this.auto_highlight();
289 this.auto_highlight();
289 // We used to need an additional refresh() after the focus, but
290 // We used to need an additional refresh() after the focus, but
290 // it appears that this has been fixed in CM. This bug would show
291 // it appears that this has been fixed in CM. This bug would show
291 // up on FF when a newly loaded markdown cell was edited.
292 // up on FF when a newly loaded markdown cell was edited.
292 };
293 };
293
294
294
295
295 CodeCell.prototype.select_all = function () {
296 CodeCell.prototype.select_all = function () {
296 var start = {line: 0, ch: 0};
297 var start = {line: 0, ch: 0};
297 var nlines = this.code_mirror.lineCount();
298 var nlines = this.code_mirror.lineCount();
298 var last_line = this.code_mirror.getLine(nlines-1);
299 var last_line = this.code_mirror.getLine(nlines-1);
299 var end = {line: nlines-1, ch: last_line.length};
300 var end = {line: nlines-1, ch: last_line.length};
300 this.code_mirror.setSelection(start, end);
301 this.code_mirror.setSelection(start, end);
301 };
302 };
302
303
303
304
304 CodeCell.prototype.collapse = function () {
305 CodeCell.prototype.collapse = function () {
305 this.collapsed = true;
306 this.collapsed = true;
306 this.output_area.collapse();
307 this.output_area.collapse();
307 };
308 };
308
309
309
310
310 CodeCell.prototype.expand = function () {
311 CodeCell.prototype.expand = function () {
311 this.collapsed = false;
312 this.collapsed = false;
312 this.output_area.expand();
313 this.output_area.expand();
313 };
314 };
314
315
315
316
316 CodeCell.prototype.toggle_output = function () {
317 CodeCell.prototype.toggle_output = function () {
317 this.collapsed = Boolean(1 - this.collapsed);
318 this.collapsed = Boolean(1 - this.collapsed);
318 this.output_area.toggle_output();
319 this.output_area.toggle_output();
319 };
320 };
320
321
321
322
322 CodeCell.prototype.toggle_output_scroll = function () {
323 CodeCell.prototype.toggle_output_scroll = function () {
323 this.output_area.toggle_scroll();
324 this.output_area.toggle_scroll();
324 };
325 };
325
326
326
327
327 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
328 CodeCell.input_prompt_classical = function (prompt_value, lines_number) {
328 var ns = prompt_value || "&nbsp;";
329 var ns = prompt_value || "&nbsp;";
329 return 'In&nbsp;[' + ns + ']:'
330 return 'In&nbsp;[' + ns + ']:'
330 };
331 };
331
332
332 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
333 CodeCell.input_prompt_continuation = function (prompt_value, lines_number) {
333 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
334 var html = [CodeCell.input_prompt_classical(prompt_value, lines_number)];
334 for(var i=1; i < lines_number; i++){html.push(['...:'])};
335 for(var i=1; i < lines_number; i++){html.push(['...:'])};
335 return html.join('</br>')
336 return html.join('</br>')
336 };
337 };
337
338
338 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
339 CodeCell.input_prompt_function = CodeCell.input_prompt_classical;
339
340
340
341
341 CodeCell.prototype.set_input_prompt = function (number) {
342 CodeCell.prototype.set_input_prompt = function (number) {
342 var nline = 1
343 var nline = 1
343 if( this.code_mirror != undefined) {
344 if( this.code_mirror != undefined) {
344 nline = this.code_mirror.lineCount();
345 nline = this.code_mirror.lineCount();
345 }
346 }
346 this.input_prompt_number = number;
347 this.input_prompt_number = number;
347 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
348 var prompt_html = CodeCell.input_prompt_function(this.input_prompt_number, nline);
348 this.element.find('div.input_prompt').html(prompt_html);
349 this.element.find('div.input_prompt').html(prompt_html);
349 };
350 };
350
351
351
352
352 CodeCell.prototype.clear_input = function () {
353 CodeCell.prototype.clear_input = function () {
353 this.code_mirror.setValue('');
354 this.code_mirror.setValue('');
354 };
355 };
355
356
356
357
357 CodeCell.prototype.get_text = function () {
358 CodeCell.prototype.get_text = function () {
358 return this.code_mirror.getValue();
359 return this.code_mirror.getValue();
359 };
360 };
360
361
361
362
362 CodeCell.prototype.set_text = function (code) {
363 CodeCell.prototype.set_text = function (code) {
363 return this.code_mirror.setValue(code);
364 return this.code_mirror.setValue(code);
364 };
365 };
365
366
366
367
367 CodeCell.prototype.at_top = function () {
368 CodeCell.prototype.at_top = function () {
368 var cursor = this.code_mirror.getCursor();
369 var cursor = this.code_mirror.getCursor();
369 if (cursor.line === 0 && cursor.ch === 0) {
370 if (cursor.line === 0 && cursor.ch === 0) {
370 return true;
371 return true;
371 } else {
372 } else {
372 return false;
373 return false;
373 }
374 }
374 };
375 };
375
376
376
377
377 CodeCell.prototype.at_bottom = function () {
378 CodeCell.prototype.at_bottom = function () {
378 var cursor = this.code_mirror.getCursor();
379 var cursor = this.code_mirror.getCursor();
379 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
380 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
380 return true;
381 return true;
381 } else {
382 } else {
382 return false;
383 return false;
383 }
384 }
384 };
385 };
385
386
386
387
387 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
388 CodeCell.prototype.clear_output = function (stdout, stderr, other) {
388 this.output_area.clear_output(stdout, stderr, other);
389 this.output_area.clear_output(stdout, stderr, other);
389 };
390 };
390
391
391
392
392 // JSON serialization
393 // JSON serialization
393
394
394 CodeCell.prototype.fromJSON = function (data) {
395 CodeCell.prototype.fromJSON = function (data) {
395 IPython.Cell.prototype.fromJSON.apply(this, arguments);
396 IPython.Cell.prototype.fromJSON.apply(this, arguments);
396 if (data.cell_type === 'code') {
397 if (data.cell_type === 'code') {
397 if (data.input !== undefined) {
398 if (data.input !== undefined) {
398 this.set_text(data.input);
399 this.set_text(data.input);
399 // make this value the starting point, so that we can only undo
400 // make this value the starting point, so that we can only undo
400 // to this state, instead of a blank cell
401 // to this state, instead of a blank cell
401 this.code_mirror.clearHistory();
402 this.code_mirror.clearHistory();
402 this.auto_highlight();
403 this.auto_highlight();
403 }
404 }
404 if (data.prompt_number !== undefined) {
405 if (data.prompt_number !== undefined) {
405 this.set_input_prompt(data.prompt_number);
406 this.set_input_prompt(data.prompt_number);
406 } else {
407 } else {
407 this.set_input_prompt();
408 this.set_input_prompt();
408 };
409 };
409 this.output_area.fromJSON(data.outputs);
410 this.output_area.fromJSON(data.outputs);
410 if (data.collapsed !== undefined) {
411 if (data.collapsed !== undefined) {
411 if (data.collapsed) {
412 if (data.collapsed) {
412 this.collapse();
413 this.collapse();
413 } else {
414 } else {
414 this.expand();
415 this.expand();
415 };
416 };
416 };
417 };
417 };
418 };
418 };
419 };
419
420
420
421
421 CodeCell.prototype.toJSON = function () {
422 CodeCell.prototype.toJSON = function () {
422 var data = IPython.Cell.prototype.toJSON.apply(this);
423 var data = IPython.Cell.prototype.toJSON.apply(this);
423 data.input = this.get_text();
424 data.input = this.get_text();
424 data.cell_type = 'code';
425 data.cell_type = 'code';
425 if (this.input_prompt_number) {
426 if (this.input_prompt_number) {
426 data.prompt_number = this.input_prompt_number;
427 data.prompt_number = this.input_prompt_number;
427 };
428 };
428 var outputs = this.output_area.toJSON();
429 var outputs = this.output_area.toJSON();
429 data.outputs = outputs;
430 data.outputs = outputs;
430 data.language = 'python';
431 data.language = 'python';
431 data.collapsed = this.collapsed;
432 data.collapsed = this.collapsed;
432 return data;
433 return data;
433 };
434 };
434
435
435
436
436 IPython.CodeCell = CodeCell;
437 IPython.CodeCell = CodeCell;
437
438
438 return IPython;
439 return IPython;
439 }(IPython));
440 }(IPython));
@@ -1,62 +1,62 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Layout
9 // Layout
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var LayoutManager = function () {
14 var LayoutManager = function () {
15 this.bind_events();
15 this.bind_events();
16 };
16 };
17
17
18
18
19 LayoutManager.prototype.bind_events = function () {
19 LayoutManager.prototype.bind_events = function () {
20 $(window).resize($.proxy(this.do_resize,this));
20 $(window).resize($.proxy(this.do_resize,this));
21 };
21 };
22
22
23 LayoutManager.prototype.app_height = function() {
23 LayoutManager.prototype.app_height = function() {
24 var win = $(window);
24 var win = $(window);
25 var w = win.width();
25 var w = win.width();
26 var h = win.height();
26 var h = win.height();
27 var header_height;
27 var header_height;
28 if ($('div#header').css('display') === 'none') {
28 if ($('div#header').css('display') === 'none') {
29 header_height = 0;
29 header_height = 0;
30 } else {
30 } else {
31 header_height = $('div#header').outerHeight(true);
31 header_height = $('div#header').outerHeight(true);
32 }
32 }
33 var menubar_height = $('div#menubar').outerHeight(true);
33 var menubar_height = $('div#menubar').outerHeight(true);
34 var toolbar_height;
34 var toolbar_height;
35 if ($('div#maintoolbar').css('display') === 'none') {
35 if ($('div#maintoolbar').css('display') === 'none') {
36 toolbar_height = 0;
36 toolbar_height = 0;
37 } else {
37 } else {
38 toolbar_height = $('div#maintoolbar').outerHeight(true);
38 toolbar_height = $('div#maintoolbar').outerHeight(true);
39 }
39 }
40 return h-header_height-menubar_height-toolbar_height; // content height
40 return h-header_height-menubar_height-toolbar_height; // content height
41 }
41 }
42
42
43 LayoutManager.prototype.do_resize = function () {
43 LayoutManager.prototype.do_resize = function () {
44 var app_height = this.app_height() // content height
44 var app_height = this.app_height() // content height
45
45
46 $('#ipython-main-app').height(app_height); // content+padding+border height
46 $('#ipython-main-app').height(app_height); // content+padding+border height
47
47
48 var pager_height = IPython.pager.percentage_height*app_height;
48 var pager_height = IPython.pager.percentage_height*app_height;
49 var pager_splitter_height = $('div#pager_splitter').outerHeight(true);
49 var pager_splitter_height = $('div#pager_splitter').outerHeight(true);
50 $('div#pager').height(pager_height);
50 $('div#pager').outerHeight(pager_height);
51 if (IPython.pager.expanded) {
51 if (IPython.pager.expanded) {
52 $('div#notebook').height(app_height-pager_height-pager_splitter_height);
52 $('div#notebook').outerHeight(app_height-pager_height-pager_splitter_height);
53 } else {
53 } else {
54 $('div#notebook').height(app_height-pager_splitter_height);
54 $('div#notebook').outerHeight(app_height-pager_splitter_height);
55 }
55 }
56 };
56 };
57
57
58 IPython.LayoutManager = LayoutManager;
58 IPython.LayoutManager = LayoutManager;
59
59
60 return IPython;
60 return IPython;
61
61
62 }(IPython));
62 }(IPython));
@@ -1,125 +1,123 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // On document ready
9 // On document ready
10 //============================================================================
10 //============================================================================
11 "use strict";
11 "use strict";
12
12
13 // for the time beeing, we have to pass marked as a parameter here,
13 // for the time beeing, we have to pass marked as a parameter here,
14 // as injecting require.js make marked not to put itself in the globals,
14 // as injecting require.js make marked not to put itself in the globals,
15 // which make both this file fail at setting marked configuration, and textcell.js
15 // which make both this file fail at setting marked configuration, and textcell.js
16 // which search marked into global.
16 // which search marked into global.
17 require(['components/marked/lib/marked'],
17 require(['components/marked/lib/marked'],
18
18
19 function (marked) {
19 function (marked) {
20
20
21 window.marked = marked
21 window.marked = marked
22
22
23 // monkey patch CM to be able to syntax highlight cell magics
23 // monkey patch CM to be able to syntax highlight cell magics
24 // bug reported upstream,
24 // bug reported upstream,
25 // see https://github.com/marijnh/CodeMirror2/issues/670
25 // see https://github.com/marijnh/CodeMirror2/issues/670
26 if(CodeMirror.getMode(1,'text/plain').indent == undefined ){
26 if(CodeMirror.getMode(1,'text/plain').indent == undefined ){
27 console.log('patching CM for undefined indent');
27 console.log('patching CM for undefined indent');
28 CodeMirror.modes.null = function() {
28 CodeMirror.modes.null = function() {
29 return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0}}
29 return {token: function(stream) {stream.skipToEnd();},indent : function(){return 0}}
30 }
30 }
31 }
31 }
32
32
33 CodeMirror.patchedGetMode = function(config, mode){
33 CodeMirror.patchedGetMode = function(config, mode){
34 var cmmode = CodeMirror.getMode(config, mode);
34 var cmmode = CodeMirror.getMode(config, mode);
35 if(cmmode.indent == null)
35 if(cmmode.indent == null)
36 {
36 {
37 console.log('patch mode "' , mode, '" on the fly');
37 console.log('patch mode "' , mode, '" on the fly');
38 cmmode.indent = function(){return 0};
38 cmmode.indent = function(){return 0};
39 }
39 }
40 return cmmode;
40 return cmmode;
41 }
41 }
42 // end monkey patching CodeMirror
42 // end monkey patching CodeMirror
43
43
44 IPython.mathjaxutils.init();
44 IPython.mathjaxutils.init();
45
45
46 IPython.read_only = $('body').data('readOnly') === 'True';
46 IPython.read_only = $('body').data('readOnly') === 'True';
47 $('#ipython-main-app').addClass('border-box-sizing');
47 $('#ipython-main-app').addClass('border-box-sizing');
48 $('div#notebook_panel').addClass('border-box-sizing');
48 $('div#notebook_panel').addClass('border-box-sizing');
49 // The header's bottom border is provided by the menu bar so we remove it.
50 $('div#header').css('border-bottom-style','none');
51
49
52 var baseProjectUrl = $('body').data('baseProjectUrl')
50 var baseProjectUrl = $('body').data('baseProjectUrl')
53
51
54 IPython.page = new IPython.Page();
52 IPython.page = new IPython.Page();
55 IPython.layout_manager = new IPython.LayoutManager();
53 IPython.layout_manager = new IPython.LayoutManager();
56 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
54 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
57 IPython.quick_help = new IPython.QuickHelp();
55 IPython.quick_help = new IPython.QuickHelp();
58 IPython.login_widget = new IPython.LoginWidget('span#login_widget',{baseProjectUrl:baseProjectUrl});
56 IPython.login_widget = new IPython.LoginWidget('span#login_widget',{baseProjectUrl:baseProjectUrl});
59 IPython.notebook = new IPython.Notebook('div#notebook',{baseProjectUrl:baseProjectUrl, read_only:IPython.read_only});
57 IPython.notebook = new IPython.Notebook('div#notebook',{baseProjectUrl:baseProjectUrl, read_only:IPython.read_only});
60 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
58 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
61 IPython.menubar = new IPython.MenuBar('#menubar',{baseProjectUrl:baseProjectUrl})
59 IPython.menubar = new IPython.MenuBar('#menubar',{baseProjectUrl:baseProjectUrl})
62 IPython.toolbar = new IPython.MainToolBar('#maintoolbar')
60 IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container')
63 IPython.tooltip = new IPython.Tooltip()
61 IPython.tooltip = new IPython.Tooltip()
64 IPython.notification_area = new IPython.NotificationArea('#notification_area')
62 IPython.notification_area = new IPython.NotificationArea('#notification_area')
65 IPython.notification_area.init_notification_widgets();
63 IPython.notification_area.init_notification_widgets();
66
64
67 IPython.layout_manager.do_resize();
65 IPython.layout_manager.do_resize();
68
66
69 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
67 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
70 '<span id="test2" style="font-weight: bold;">x</span>'+
68 '<span id="test2" style="font-weight: bold;">x</span>'+
71 '<span id="test3" style="font-style: italic;">x</span></pre></div>')
69 '<span id="test3" style="font-style: italic;">x</span></pre></div>')
72 var nh = $('#test1').innerHeight();
70 var nh = $('#test1').innerHeight();
73 var bh = $('#test2').innerHeight();
71 var bh = $('#test2').innerHeight();
74 var ih = $('#test3').innerHeight();
72 var ih = $('#test3').innerHeight();
75 if(nh != bh || nh != ih) {
73 if(nh != bh || nh != ih) {
76 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
74 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
77 }
75 }
78 $('#fonttest').remove();
76 $('#fonttest').remove();
79
77
80 if(IPython.read_only){
78 if(IPython.read_only){
81 // hide various elements from read-only view
79 // hide various elements from read-only view
82 $('div#pager').remove();
80 $('div#pager').remove();
83 $('div#pager_splitter').remove();
81 $('div#pager_splitter').remove();
84
82
85 // set the notebook name field as not modifiable
83 // set the notebook name field as not modifiable
86 $('#notebook_name').attr('disabled','disabled')
84 $('#notebook_name').attr('disabled','disabled')
87 }
85 }
88
86
89 IPython.page.show();
87 IPython.page.show();
90
88
91 IPython.layout_manager.do_resize();
89 IPython.layout_manager.do_resize();
92 var first_load = function () {
90 var first_load = function () {
93 IPython.layout_manager.do_resize();
91 IPython.layout_manager.do_resize();
94 var hash = document.location.hash;
92 var hash = document.location.hash;
95 if (hash) {
93 if (hash) {
96 document.location.hash = '';
94 document.location.hash = '';
97 document.location.hash = hash;
95 document.location.hash = hash;
98 }
96 }
99 IPython.notebook.set_autosave_interval(IPython.notebook.minimum_autosave_interval);
97 IPython.notebook.set_autosave_interval(IPython.notebook.minimum_autosave_interval);
100 // only do this once
98 // only do this once
101 $([IPython.events]).off('notebook_loaded.Notebook', first_load);
99 $([IPython.events]).off('notebook_loaded.Notebook', first_load);
102 };
100 };
103
101
104 $([IPython.events]).on('notebook_loaded.Notebook', first_load);
102 $([IPython.events]).on('notebook_loaded.Notebook', first_load);
105 IPython.notebook.load_notebook($('body').data('notebookId'));
103 IPython.notebook.load_notebook($('body').data('notebookId'));
106
104
107 if (marked) {
105 if (marked) {
108 marked.setOptions({
106 marked.setOptions({
109 gfm : true,
107 gfm : true,
110 tables: true,
108 tables: true,
111 langPrefix: "language-",
109 langPrefix: "language-",
112 highlight: function(code, lang) {
110 highlight: function(code, lang) {
113 var highlighted;
111 var highlighted;
114 try {
112 try {
115 highlighted = hljs.highlight(lang, code, false);
113 highlighted = hljs.highlight(lang, code, false);
116 } catch(err) {
114 } catch(err) {
117 highlighted = hljs.highlightAuto(code);
115 highlighted = hljs.highlightAuto(code);
118 }
116 }
119 return highlighted.value;
117 return highlighted.value;
120 }
118 }
121 })
119 })
122 }
120 }
123 }
121 }
124
122
125 );
123 );
@@ -1,206 +1,206 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
2 // Copyright (C) 2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // ToolBar
9 // ToolBar
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var MainToolBar = function (selector) {
14 var MainToolBar = function (selector) {
15 IPython.ToolBar.apply(this, arguments);
15 IPython.ToolBar.apply(this, arguments);
16 this.construct();
16 this.construct();
17 this.add_celltype_list();
17 this.add_celltype_list();
18 this.add_celltoolbar_list();
18 this.add_celltoolbar_list();
19 this.bind_events();
19 this.bind_events();
20 };
20 };
21
21
22 MainToolBar.prototype = new IPython.ToolBar();
22 MainToolBar.prototype = new IPython.ToolBar();
23
23
24 MainToolBar.prototype.construct = function () {
24 MainToolBar.prototype.construct = function () {
25 this.add_buttons_group([
25 this.add_buttons_group([
26 {
26 {
27 id : 'save_b',
27 id : 'save_b',
28 label : 'Save and Checkpoint',
28 label : 'Save and Checkpoint',
29 icon : 'ui-icon-disk',
29 icon : 'icon-hdd',
30 callback : function () {
30 callback : function () {
31 IPython.notebook.save_checkpoint();
31 IPython.notebook.save_checkpoint();
32 }
32 }
33 }
33 }
34 ]);
34 ]);
35 this.add_buttons_group([
35 this.add_buttons_group([
36 {
36 {
37 id : 'cut_b',
37 id : 'cut_b',
38 label : 'Cut Cell',
38 label : 'Cut Cell',
39 icon : 'ui-icon-scissors',
39 icon : 'icon-cut',
40 callback : function () {
40 callback : function () {
41 IPython.notebook.cut_cell();
41 IPython.notebook.cut_cell();
42 }
42 }
43 },
43 },
44 {
44 {
45 id : 'copy_b',
45 id : 'copy_b',
46 label : 'Copy Cell',
46 label : 'Copy Cell',
47 icon : 'ui-icon-copy',
47 icon : 'icon-copy',
48 callback : function () {
48 callback : function () {
49 IPython.notebook.copy_cell();
49 IPython.notebook.copy_cell();
50 }
50 }
51 },
51 },
52 {
52 {
53 id : 'paste_b',
53 id : 'paste_b',
54 label : 'Paste Cell Below',
54 label : 'Paste Cell Below',
55 icon : 'ui-icon-clipboard',
55 icon : 'icon-paste',
56 callback : function () {
56 callback : function () {
57 IPython.notebook.paste_cell_below();
57 IPython.notebook.paste_cell_below();
58 }
58 }
59 }
59 }
60 ],'cut_copy_paste');
60 ],'cut_copy_paste');
61
61
62 this.add_buttons_group([
62 this.add_buttons_group([
63 {
63 {
64 id : 'move_up_b',
64 id : 'move_up_b',
65 label : 'Move Cell Up',
65 label : 'Move Cell Up',
66 icon : 'ui-icon-arrowthick-1-n',
66 icon : 'icon-arrow-up',
67 callback : function () {
67 callback : function () {
68 IPython.notebook.move_cell_up();
68 IPython.notebook.move_cell_up();
69 }
69 }
70 },
70 },
71 {
71 {
72 id : 'move_down_b',
72 id : 'move_down_b',
73 label : 'Move Cell Down',
73 label : 'Move Cell Down',
74 icon : 'ui-icon-arrowthick-1-s',
74 icon : 'icon-arrow-down',
75 callback : function () {
75 callback : function () {
76 IPython.notebook.move_cell_down();
76 IPython.notebook.move_cell_down();
77 }
77 }
78 }
78 }
79 ],'move_up_down');
79 ],'move_up_down');
80
80
81 this.add_buttons_group([
81 this.add_buttons_group([
82 {
82 {
83 id : 'insert_above_b',
83 id : 'insert_above_b',
84 label : 'Insert Cell Above',
84 label : 'Insert Cell Above',
85 icon : 'ui-icon-arrowthickstop-1-n',
85 icon : 'icon-circle-arrow-up',
86 callback : function () {
86 callback : function () {
87 IPython.notebook.insert_cell_above('code');
87 IPython.notebook.insert_cell_above('code');
88 }
88 }
89 },
89 },
90 {
90 {
91 id : 'insert_below_b',
91 id : 'insert_below_b',
92 label : 'Insert Cell Below',
92 label : 'Insert Cell Below',
93 icon : 'ui-icon-arrowthickstop-1-s',
93 icon : 'icon-circle-arrow-down',
94 callback : function () {
94 callback : function () {
95 IPython.notebook.insert_cell_below('code');
95 IPython.notebook.insert_cell_below('code');
96 }
96 }
97 }
97 }
98 ],'insert_above_below');
98 ],'insert_above_below');
99
99
100 this.add_buttons_group([
100 this.add_buttons_group([
101 {
101 {
102 id : 'run_b',
102 id : 'run_b',
103 label : 'Run Cell',
103 label : 'Run Cell',
104 icon : 'ui-icon-play',
104 icon : 'icon-play',
105 callback : function () {
105 callback : function () {
106 IPython.notebook.execute_selected_cell();
106 IPython.notebook.execute_selected_cell();
107 }
107 }
108 },
108 },
109 {
109 {
110 id : 'interrupt_b',
110 id : 'interrupt_b',
111 label : 'Interrupt',
111 label : 'Interrupt',
112 icon : 'ui-icon-stop',
112 icon : 'icon-stop',
113 callback : function () {
113 callback : function () {
114 IPython.notebook.kernel.interrupt();
114 IPython.notebook.kernel.interrupt();
115 }
115 }
116 }
116 }
117 ],'run_int');
117 ],'run_int');
118 };
118 };
119
119
120 MainToolBar.prototype.add_celltype_list = function () {
120 MainToolBar.prototype.add_celltype_list = function () {
121 this.element
121 this.element
122 .append($('<select/>')
122 .append($('<select/>')
123 .attr('id','cell_type')
123 .attr('id','cell_type')
124 .addClass('ui-widget-content')
124 // .addClass('ui-widget-content')
125 .append($('<option/>').attr('value','code').text('Code'))
125 .append($('<option/>').attr('value','code').text('Code'))
126 .append($('<option/>').attr('value','markdown').text('Markdown'))
126 .append($('<option/>').attr('value','markdown').text('Markdown'))
127 .append($('<option/>').attr('value','raw').text('Raw Text'))
127 .append($('<option/>').attr('value','raw').text('Raw Text'))
128 .append($('<option/>').attr('value','heading1').text('Heading 1'))
128 .append($('<option/>').attr('value','heading1').text('Heading 1'))
129 .append($('<option/>').attr('value','heading2').text('Heading 2'))
129 .append($('<option/>').attr('value','heading2').text('Heading 2'))
130 .append($('<option/>').attr('value','heading3').text('Heading 3'))
130 .append($('<option/>').attr('value','heading3').text('Heading 3'))
131 .append($('<option/>').attr('value','heading4').text('Heading 4'))
131 .append($('<option/>').attr('value','heading4').text('Heading 4'))
132 .append($('<option/>').attr('value','heading5').text('Heading 5'))
132 .append($('<option/>').attr('value','heading5').text('Heading 5'))
133 .append($('<option/>').attr('value','heading6').text('Heading 6'))
133 .append($('<option/>').attr('value','heading6').text('Heading 6'))
134 );
134 );
135 };
135 };
136
136
137
137
138 MainToolBar.prototype.add_celltoolbar_list = function () {
138 MainToolBar.prototype.add_celltoolbar_list = function () {
139 var label = $('<label/>').text('Cell Toolbar:');
139 var label = $('<span/>').addClass("navbar-text").text('Cell Toolbar:');
140 var select = $('<select/>')
140 var select = $('<select/>')
141 .addClass('ui-widget-content')
141 // .addClass('ui-widget-content')
142 .attr('id', 'ctb_select')
142 .attr('id', 'ctb_select')
143 .append($('<option/>').attr('value', '').text('None'));
143 .append($('<option/>').attr('value', '').text('None'));
144 this.element.append(label).append(select);
144 this.element.append(label).append(select);
145 select.change(function() {
145 select.change(function() {
146 var val = $(this).val()
146 var val = $(this).val()
147 if (val =='') {
147 if (val =='') {
148 IPython.CellToolbar.global_hide();
148 IPython.CellToolbar.global_hide();
149 } else {
149 } else {
150 IPython.CellToolbar.global_show();
150 IPython.CellToolbar.global_show();
151 IPython.CellToolbar.activate_preset(val);
151 IPython.CellToolbar.activate_preset(val);
152 }
152 }
153 });
153 });
154 // Setup the currently registered presets.
154 // Setup the currently registered presets.
155 var presets = IPython.CellToolbar.list_presets();
155 var presets = IPython.CellToolbar.list_presets();
156 for (var i=0; i<presets.length; i++) {
156 for (var i=0; i<presets.length; i++) {
157 var name = presets[i];
157 var name = presets[i];
158 select.append($('<option/>').attr('value', name).text(name));
158 select.append($('<option/>').attr('value', name).text(name));
159 }
159 }
160 // Setup future preset registrations.
160 // Setup future preset registrations.
161 $([IPython.events]).on('preset_added.CellToolbar', function (event, data) {
161 $([IPython.events]).on('preset_added.CellToolbar', function (event, data) {
162 var name = data.name;
162 var name = data.name;
163 select.append($('<option/>').attr('value', name).text(name));
163 select.append($('<option/>').attr('value', name).text(name));
164 });
164 });
165 };
165 };
166
166
167
167
168 MainToolBar.prototype.bind_events = function () {
168 MainToolBar.prototype.bind_events = function () {
169 var that = this;
169 var that = this;
170
170
171 this.element.find('#cell_type').change(function () {
171 this.element.find('#cell_type').change(function () {
172 var cell_type = $(this).val();
172 var cell_type = $(this).val();
173 if (cell_type === 'code') {
173 if (cell_type === 'code') {
174 IPython.notebook.to_code();
174 IPython.notebook.to_code();
175 } else if (cell_type === 'markdown') {
175 } else if (cell_type === 'markdown') {
176 IPython.notebook.to_markdown();
176 IPython.notebook.to_markdown();
177 } else if (cell_type === 'raw') {
177 } else if (cell_type === 'raw') {
178 IPython.notebook.to_raw();
178 IPython.notebook.to_raw();
179 } else if (cell_type === 'heading1') {
179 } else if (cell_type === 'heading1') {
180 IPython.notebook.to_heading(undefined, 1);
180 IPython.notebook.to_heading(undefined, 1);
181 } else if (cell_type === 'heading2') {
181 } else if (cell_type === 'heading2') {
182 IPython.notebook.to_heading(undefined, 2);
182 IPython.notebook.to_heading(undefined, 2);
183 } else if (cell_type === 'heading3') {
183 } else if (cell_type === 'heading3') {
184 IPython.notebook.to_heading(undefined, 3);
184 IPython.notebook.to_heading(undefined, 3);
185 } else if (cell_type === 'heading4') {
185 } else if (cell_type === 'heading4') {
186 IPython.notebook.to_heading(undefined, 4);
186 IPython.notebook.to_heading(undefined, 4);
187 } else if (cell_type === 'heading5') {
187 } else if (cell_type === 'heading5') {
188 IPython.notebook.to_heading(undefined, 5);
188 IPython.notebook.to_heading(undefined, 5);
189 } else if (cell_type === 'heading6') {
189 } else if (cell_type === 'heading6') {
190 IPython.notebook.to_heading(undefined, 6);
190 IPython.notebook.to_heading(undefined, 6);
191 }
191 }
192 });
192 });
193 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
193 $([IPython.events]).on('selected_cell_type_changed.Notebook', function (event, data) {
194 if (data.cell_type === 'heading') {
194 if (data.cell_type === 'heading') {
195 that.element.find('#cell_type').val(data.cell_type+data.level);
195 that.element.find('#cell_type').val(data.cell_type+data.level);
196 } else {
196 } else {
197 that.element.find('#cell_type').val(data.cell_type);
197 that.element.find('#cell_type').val(data.cell_type);
198 }
198 }
199 });
199 });
200 };
200 };
201
201
202 IPython.MainToolBar = MainToolBar;
202 IPython.MainToolBar = MainToolBar;
203
203
204 return IPython;
204 return IPython;
205
205
206 }(IPython));
206 }(IPython));
@@ -1,242 +1,245 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2012 The IPython Development Team
2 // Copyright (C) 2008-2012 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // MathJax utility functions
9 // MathJax utility functions
10 //============================================================================
10 //============================================================================
11
11
12 IPython.namespace('IPython.mathjaxutils');
12 IPython.namespace('IPython.mathjaxutils');
13
13
14 IPython.mathjaxutils = (function (IPython) {
14 IPython.mathjaxutils = (function (IPython) {
15
15
16 var init = function () {
16 var init = function () {
17 if (window.MathJax) {
17 if (window.MathJax) {
18 // MathJax loaded
18 // MathJax loaded
19 MathJax.Hub.Config({
19 MathJax.Hub.Config({
20 tex2jax: {
20 tex2jax: {
21 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
21 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
22 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
22 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
23 processEscapes: true,
23 processEscapes: true,
24 processEnvironments: true
24 processEnvironments: true
25 },
25 },
26 displayAlign: 'left', // Change this to 'center' to center equations.
26 displayAlign: 'left', // Change this to 'center' to center equations.
27 "HTML-CSS": {
27 "HTML-CSS": {
28 styles: {'.MathJax_Display': {"margin": 0}}
28 styles: {'.MathJax_Display': {"margin": 0}}
29 }
29 }
30 });
30 });
31 MathJax.Hub.Configured();
31 MathJax.Hub.Configured();
32 } else if (window.mathjax_url != "") {
32 } else if (window.mathjax_url != "") {
33 // Don't have MathJax, but should. Show dialog.
33 // Don't have MathJax, but should. Show dialog.
34 var dialog = $('<div></div>')
34 var message = $('<div/>')
35 .append(
35 .append(
36 $("<p></p>").addClass('dialog').html(
36 $("<p/></p>").addClass('dialog').html(
37 "Math/LaTeX rendering will be disabled."
37 "Math/LaTeX rendering will be disabled."
38 )
38 )
39 ).append(
39 ).append(
40 $("<p></p>").addClass('dialog').html(
40 $("<p></p>").addClass('dialog').html(
41 "If you have administrative access to the notebook server and" +
41 "If you have administrative access to the notebook server and" +
42 " a working internet connection, you can install a local copy" +
42 " a working internet connection, you can install a local copy" +
43 " of MathJax for offline use with the following command on the server" +
43 " of MathJax for offline use with the following command on the server" +
44 " at a Python or IPython prompt:"
44 " at a Python or IPython prompt:"
45 )
45 )
46 ).append(
46 ).append(
47 $("<pre></pre>").addClass('dialog').html(
47 $("<pre></pre>").addClass('dialog').html(
48 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
48 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
49 )
49 )
50 ).append(
50 ).append(
51 $("<p></p>").addClass('dialog').html(
51 $("<p></p>").addClass('dialog').html(
52 "This will try to install MathJax into the IPython source directory."
52 "This will try to install MathJax into the IPython source directory."
53 )
53 )
54 ).append(
54 ).append(
55 $("<p></p>").addClass('dialog').html(
55 $("<p></p>").addClass('dialog').html(
56 "If IPython is installed to a location that requires" +
56 "If IPython is installed to a location that requires" +
57 " administrative privileges to write, you will need to make this call as" +
57 " administrative privileges to write, you will need to make this call as" +
58 " an administrator, via 'sudo'."
58 " an administrator, via 'sudo'."
59 )
59 )
60 ).append(
60 ).append(
61 $("<p></p>").addClass('dialog').html(
61 $("<p></p>").addClass('dialog').html(
62 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
62 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
63 )
63 )
64 ).append(
64 ).append(
65 $("<pre></pre>").addClass('dialog').html(
65 $("<pre></pre>").addClass('dialog').html(
66 "$ ipython notebook --no-mathjax"
66 "$ ipython notebook --no-mathjax"
67 )
67 )
68 ).append(
68 ).append(
69 $("<p></p>").addClass('dialog').html(
69 $("<p></p>").addClass('dialog').html(
70 "which will prevent this dialog from appearing."
70 "which will prevent this dialog from appearing."
71 )
71 )
72 ).dialog({
72 )
73 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
73 IPython.dialog.modal({
74 width: "70%",
74 title : "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
75 modal: true,
75 body : message,
76 })
76 buttons : {
77 OK : {class: "btn-danger"}
78 }
79 });
77 } else {
80 } else {
78 // No MathJax, but none expected. No dialog.
81 // No MathJax, but none expected. No dialog.
79 };
82 };
80 };
83 };
81
84
82 // Some magic for deferring mathematical expressions to MathJax
85 // Some magic for deferring mathematical expressions to MathJax
83 // by hiding them from the Markdown parser.
86 // by hiding them from the Markdown parser.
84 // Some of the code here is adapted with permission from Davide Cervone
87 // Some of the code here is adapted with permission from Davide Cervone
85 // under the terms of the Apache2 license governing the MathJax project.
88 // under the terms of the Apache2 license governing the MathJax project.
86 // Other minor modifications are also due to StackExchange and are used with
89 // Other minor modifications are also due to StackExchange and are used with
87 // permission.
90 // permission.
88
91
89 var inline = "$"; // the inline math delimiter
92 var inline = "$"; // the inline math delimiter
90 var blocks, start, end, last, braces; // used in searching for math
93 var blocks, start, end, last, braces; // used in searching for math
91 var math; // stores math until pagedown (Markdown parser) is done
94 var math; // stores math until pagedown (Markdown parser) is done
92
95
93 // MATHSPLIT contains the pattern for math delimiters and special symbols
96 // MATHSPLIT contains the pattern for math delimiters and special symbols
94 // needed for searching for math in the text input.
97 // needed for searching for math in the text input.
95 var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
98 var MATHSPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
96
99
97 // The math is in blocks i through j, so
100 // The math is in blocks i through j, so
98 // collect it into one block and clear the others.
101 // collect it into one block and clear the others.
99 // Replace &, <, and > by named entities.
102 // Replace &, <, and > by named entities.
100 // For IE, put <br> at the ends of comments since IE removes \n.
103 // For IE, put <br> at the ends of comments since IE removes \n.
101 // Clear the current math positions and store the index of the
104 // Clear the current math positions and store the index of the
102 // math, then push the math string onto the storage array.
105 // math, then push the math string onto the storage array.
103 // The preProcess function is called on all blocks if it has been passed in
106 // The preProcess function is called on all blocks if it has been passed in
104 var process_math = function (i, j, pre_process) {
107 var process_math = function (i, j, pre_process) {
105 var hub = MathJax.Hub;
108 var hub = MathJax.Hub;
106 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
109 var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use HTML entity for &
107 .replace(/</g, "&lt;") // use HTML entity for <
110 .replace(/</g, "&lt;") // use HTML entity for <
108 .replace(/>/g, "&gt;") // use HTML entity for >
111 .replace(/>/g, "&gt;") // use HTML entity for >
109 ;
112 ;
110 if (hub.Browser.isMSIE) {
113 if (hub.Browser.isMSIE) {
111 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
114 block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
112 }
115 }
113 while (j > i) {
116 while (j > i) {
114 blocks[j] = "";
117 blocks[j] = "";
115 j--;
118 j--;
116 }
119 }
117 blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
120 blocks[i] = "@@" + math.length + "@@"; // replace the current block text with a unique tag to find later
118 if (pre_process)
121 if (pre_process)
119 block = pre_process(block);
122 block = pre_process(block);
120 math.push(block);
123 math.push(block);
121 start = end = last = null;
124 start = end = last = null;
122 }
125 }
123
126
124 // Break up the text into its component parts and search
127 // Break up the text into its component parts and search
125 // through them for math delimiters, braces, linebreaks, etc.
128 // through them for math delimiters, braces, linebreaks, etc.
126 // Math delimiters must match and braces must balance.
129 // Math delimiters must match and braces must balance.
127 // Don't allow math to pass through a double linebreak
130 // Don't allow math to pass through a double linebreak
128 // (which will be a paragraph).
131 // (which will be a paragraph).
129 //
132 //
130 var remove_math = function (text) {
133 var remove_math = function (text) {
131 if (!window.MathJax) {
134 if (!window.MathJax) {
132 return text;
135 return text;
133 }
136 }
134
137
135 start = end = last = null; // for tracking math delimiters
138 start = end = last = null; // for tracking math delimiters
136 math = []; // stores math strings for later
139 math = []; // stores math strings for later
137
140
138 // Except for extreme edge cases, this should catch precisely those pieces of the markdown
141 // Except for extreme edge cases, this should catch precisely those pieces of the markdown
139 // source that will later be turned into code spans. While MathJax will not TeXify code spans,
142 // source that will later be turned into code spans. While MathJax will not TeXify code spans,
140 // we still have to consider them at this point; the following issue has happened several times:
143 // we still have to consider them at this point; the following issue has happened several times:
141 //
144 //
142 // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
145 // `$foo` and `$bar` are varibales. --> <code>$foo ` and `$bar</code> are variables.
143
146
144 var hasCodeSpans = /`/.test(text),
147 var hasCodeSpans = /`/.test(text),
145 de_tilde;
148 de_tilde;
146 if (hasCodeSpans) {
149 if (hasCodeSpans) {
147 text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
150 text = text.replace(/~/g, "~T").replace(/(^|[^\\])(`+)([^\n]*?[^`\n])\2(?!`)/gm, function (wholematch) {
148 return wholematch.replace(/\$/g, "~D");
151 return wholematch.replace(/\$/g, "~D");
149 });
152 });
150 de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) };
153 de_tilde = function (text) { return text.replace(/~([TD])/g, function (wholematch, character) { return { T: "~", D: "$" }[character]; }) };
151 } else {
154 } else {
152 de_tilde = function (text) { return text; };
155 de_tilde = function (text) { return text; };
153 }
156 }
154
157
155 blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
158 blocks = IPython.utils.regex_split(text.replace(/\r\n?/g, "\n"),MATHSPLIT);
156
159
157 for (var i = 1, m = blocks.length; i < m; i += 2) {
160 for (var i = 1, m = blocks.length; i < m; i += 2) {
158 var block = blocks[i];
161 var block = blocks[i];
159 if (block.charAt(0) === "@") {
162 if (block.charAt(0) === "@") {
160 //
163 //
161 // Things that look like our math markers will get
164 // Things that look like our math markers will get
162 // stored and then retrieved along with the math.
165 // stored and then retrieved along with the math.
163 //
166 //
164 blocks[i] = "@@" + math.length + "@@";
167 blocks[i] = "@@" + math.length + "@@";
165 math.push(block);
168 math.push(block);
166 }
169 }
167 else if (start) {
170 else if (start) {
168 //
171 //
169 // If we are in math, look for the end delimiter,
172 // If we are in math, look for the end delimiter,
170 // but don't go past double line breaks, and
173 // but don't go past double line breaks, and
171 // and balance braces within the math.
174 // and balance braces within the math.
172 //
175 //
173 if (block === end) {
176 if (block === end) {
174 if (braces) {
177 if (braces) {
175 last = i
178 last = i
176 }
179 }
177 else {
180 else {
178 process_math(start, i, de_tilde)
181 process_math(start, i, de_tilde)
179 }
182 }
180 }
183 }
181 else if (block.match(/\n.*\n/)) {
184 else if (block.match(/\n.*\n/)) {
182 if (last) {
185 if (last) {
183 i = last;
186 i = last;
184 process_math(start, i, de_tilde)
187 process_math(start, i, de_tilde)
185 }
188 }
186 start = end = last = null;
189 start = end = last = null;
187 braces = 0;
190 braces = 0;
188 }
191 }
189 else if (block === "{") {
192 else if (block === "{") {
190 braces++
193 braces++
191 }
194 }
192 else if (block === "}" && braces) {
195 else if (block === "}" && braces) {
193 braces--
196 braces--
194 }
197 }
195 }
198 }
196 else {
199 else {
197 //
200 //
198 // Look for math start delimiters and when
201 // Look for math start delimiters and when
199 // found, set up the end delimiter.
202 // found, set up the end delimiter.
200 //
203 //
201 if (block === inline || block === "$$") {
204 if (block === inline || block === "$$") {
202 start = i;
205 start = i;
203 end = block;
206 end = block;
204 braces = 0;
207 braces = 0;
205 }
208 }
206 else if (block.substr(1, 5) === "begin") {
209 else if (block.substr(1, 5) === "begin") {
207 start = i;
210 start = i;
208 end = "\\end" + block.substr(6);
211 end = "\\end" + block.substr(6);
209 braces = 0;
212 braces = 0;
210 }
213 }
211 }
214 }
212 }
215 }
213 if (last) {
216 if (last) {
214 process_math(start, last, de_tilde)
217 process_math(start, last, de_tilde)
215 }
218 }
216 return de_tilde(blocks.join(""));
219 return de_tilde(blocks.join(""));
217 }
220 }
218
221
219 //
222 //
220 // Put back the math strings that were saved,
223 // Put back the math strings that were saved,
221 // and clear the math array (no need to keep it around).
224 // and clear the math array (no need to keep it around).
222 //
225 //
223 var replace_math = function (text) {
226 var replace_math = function (text) {
224 if (!window.MathJax) {
227 if (!window.MathJax) {
225 return text;
228 return text;
226 }
229 }
227
230
228 text = text.replace(/@@(\d+)@@/g, function (match, n) {
231 text = text.replace(/@@(\d+)@@/g, function (match, n) {
229 return math[n]
232 return math[n]
230 });
233 });
231 math = null;
234 math = null;
232 return text;
235 return text;
233 }
236 }
234
237
235 return {
238 return {
236 init : init,
239 init : init,
237 process_math : process_math,
240 process_math : process_math,
238 remove_math : remove_math,
241 remove_math : remove_math,
239 replace_math : replace_math
242 replace_math : replace_math
240 };
243 };
241
244
242 }(IPython)); No newline at end of file
245 }(IPython));
@@ -1,264 +1,263 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // MenuBar
9 // MenuBar
10 //============================================================================
10 //============================================================================
11
11
12 /**
12 /**
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule MenuBar
15 * @submodule MenuBar
16 */
16 */
17
17
18
18
19 var IPython = (function (IPython) {
19 var IPython = (function (IPython) {
20
20
21 /**
21 /**
22 * A MenuBar Class to generate the menubar of IPython noteboko
22 * A MenuBar Class to generate the menubar of IPython noteboko
23 * @Class MenuBar
23 * @Class MenuBar
24 *
24 *
25 * @constructor
25 * @constructor
26 *
26 *
27 *
27 *
28 * @param selector {string} selector for the menubar element in DOM
28 * @param selector {string} selector for the menubar element in DOM
29 * @param {object} [options]
29 * @param {object} [options]
30 * @param [options.baseProjectUrl] {String} String to use for the
30 * @param [options.baseProjectUrl] {String} String to use for the
31 * Base Project url, default would be to inspect
31 * Base Project url, default would be to inspect
32 * $('body').data('baseProjectUrl');
32 * $('body').data('baseProjectUrl');
33 * does not support change for now is set through this option
33 * does not support change for now is set through this option
34 */
34 */
35 var MenuBar = function (selector, options) {
35 var MenuBar = function (selector, options) {
36 var options = options || {};
36 var options = options || {};
37 if(options.baseProjectUrl!= undefined){
37 if(options.baseProjectUrl!= undefined){
38 this._baseProjectUrl = options.baseProjectUrl;
38 this._baseProjectUrl = options.baseProjectUrl;
39 }
39 }
40 this.selector = selector;
40 this.selector = selector;
41 if (this.selector !== undefined) {
41 if (this.selector !== undefined) {
42 this.element = $(selector);
42 this.element = $(selector);
43 this.style();
43 this.style();
44 this.bind_events();
44 this.bind_events();
45 }
45 }
46 };
46 };
47
47
48 MenuBar.prototype.baseProjectUrl = function(){
48 MenuBar.prototype.baseProjectUrl = function(){
49 return this._baseProjectUrl || $('body').data('baseProjectUrl');
49 return this._baseProjectUrl || $('body').data('baseProjectUrl');
50 };
50 };
51
51
52
52
53 MenuBar.prototype.style = function () {
53 MenuBar.prototype.style = function () {
54 this.element.addClass('border-box-sizing');
54 this.element.addClass('border-box-sizing');
55 $('ul#menus').menubar({
55 this.element.find("li").click(function (event, ui) {
56 select : function (event, ui) {
57 // The selected cell loses focus when the menu is entered, so we
56 // The selected cell loses focus when the menu is entered, so we
58 // re-select it upon selection.
57 // re-select it upon selection.
59 var i = IPython.notebook.get_selected_index();
58 var i = IPython.notebook.get_selected_index();
60 IPython.notebook.select(i);
59 IPython.notebook.select(i);
61 }
60 }
62 });
61 );
63 this.element.find("#restore_checkpoint").find("ul").find("li").hide();
64 };
62 };
65
63
66
64
67 MenuBar.prototype.bind_events = function () {
65 MenuBar.prototype.bind_events = function () {
68 // File
66 // File
69 var that = this;
67 var that = this;
70 this.element.find('#new_notebook').click(function () {
68 this.element.find('#new_notebook').click(function () {
71 window.open(that.baseProjectUrl()+'new');
69 window.open(that.baseProjectUrl()+'new');
72 });
70 });
73 this.element.find('#open_notebook').click(function () {
71 this.element.find('#open_notebook').click(function () {
74 window.open(that.baseProjectUrl());
72 window.open(that.baseProjectUrl());
75 });
73 });
76 this.element.find('#rename_notebook').click(function () {
74 this.element.find('#rename_notebook').click(function () {
77 IPython.save_widget.rename_notebook();
75 IPython.save_widget.rename_notebook();
78 });
76 });
79 this.element.find('#copy_notebook').click(function () {
77 this.element.find('#copy_notebook').click(function () {
80 var notebook_id = IPython.notebook.get_notebook_id();
78 var notebook_id = IPython.notebook.get_notebook_id();
81 var url = that.baseProjectUrl() + notebook_id + '/copy';
79 var url = that.baseProjectUrl() + notebook_id + '/copy';
82 window.open(url,'_blank');
80 window.open(url,'_blank');
83 return false;
81 return false;
84 });
82 });
85 this.element.find('#save_checkpoint').click(function () {
83 this.element.find('#save_checkpoint').click(function () {
86 IPython.notebook.save_checkpoint();
84 IPython.notebook.save_checkpoint();
87 });
85 });
88 this.element.find('#restore_checkpoint').click(function () {
86 this.element.find('#restore_checkpoint').click(function () {
89 });
87 });
90 this.element.find('#download_ipynb').click(function () {
88 this.element.find('#download_ipynb').click(function () {
91 var notebook_id = IPython.notebook.get_notebook_id();
89 var notebook_id = IPython.notebook.get_notebook_id();
92 var url = that.baseProjectUrl() + 'notebooks/' +
90 var url = that.baseProjectUrl() + 'notebooks/' +
93 notebook_id + '?format=json';
91 notebook_id + '?format=json';
94 window.location.assign(url);
92 window.location.assign(url);
95 });
93 });
96 this.element.find('#download_py').click(function () {
94 this.element.find('#download_py').click(function () {
97 var notebook_id = IPython.notebook.get_notebook_id();
95 var notebook_id = IPython.notebook.get_notebook_id();
98 var url = that.baseProjectUrl() + 'notebooks/' +
96 var url = that.baseProjectUrl() + 'notebooks/' +
99 notebook_id + '?format=py';
97 notebook_id + '?format=py';
100 window.location.assign(url);
98 window.location.assign(url);
101 });
99 });
102 this.element.find('#kill_and_exit').click(function () {
100 this.element.find('#kill_and_exit').click(function () {
103 IPython.notebook.kernel.kill();
101 IPython.notebook.kernel.kill();
104 setTimeout(function(){window.close();}, 200);
102 setTimeout(function(){window.close();}, 200);
105 });
103 });
106 // Edit
104 // Edit
107 this.element.find('#cut_cell').click(function () {
105 this.element.find('#cut_cell').click(function () {
108 IPython.notebook.cut_cell();
106 IPython.notebook.cut_cell();
109 });
107 });
110 this.element.find('#copy_cell').click(function () {
108 this.element.find('#copy_cell').click(function () {
111 IPython.notebook.copy_cell();
109 IPython.notebook.copy_cell();
112 });
110 });
113 this.element.find('#delete_cell').click(function () {
111 this.element.find('#delete_cell').click(function () {
114 IPython.notebook.delete_cell();
112 IPython.notebook.delete_cell();
115 });
113 });
116 this.element.find('#undelete_cell').click(function () {
114 this.element.find('#undelete_cell').click(function () {
117 IPython.notebook.undelete();
115 IPython.notebook.undelete();
118 });
116 });
119 this.element.find('#split_cell').click(function () {
117 this.element.find('#split_cell').click(function () {
120 IPython.notebook.split_cell();
118 IPython.notebook.split_cell();
121 });
119 });
122 this.element.find('#merge_cell_above').click(function () {
120 this.element.find('#merge_cell_above').click(function () {
123 IPython.notebook.merge_cell_above();
121 IPython.notebook.merge_cell_above();
124 });
122 });
125 this.element.find('#merge_cell_below').click(function () {
123 this.element.find('#merge_cell_below').click(function () {
126 IPython.notebook.merge_cell_below();
124 IPython.notebook.merge_cell_below();
127 });
125 });
128 this.element.find('#move_cell_up').click(function () {
126 this.element.find('#move_cell_up').click(function () {
129 IPython.notebook.move_cell_up();
127 IPython.notebook.move_cell_up();
130 });
128 });
131 this.element.find('#move_cell_down').click(function () {
129 this.element.find('#move_cell_down').click(function () {
132 IPython.notebook.move_cell_down();
130 IPython.notebook.move_cell_down();
133 });
131 });
134 this.element.find('#select_previous').click(function () {
132 this.element.find('#select_previous').click(function () {
135 IPython.notebook.select_prev();
133 IPython.notebook.select_prev();
136 });
134 });
137 this.element.find('#select_next').click(function () {
135 this.element.find('#select_next').click(function () {
138 IPython.notebook.select_next();
136 IPython.notebook.select_next();
139 });
137 });
140 // View
138 // View
141 this.element.find('#toggle_header').click(function () {
139 this.element.find('#toggle_header').click(function () {
142 $('div#header').toggle();
140 $('div#header').toggle();
143 IPython.layout_manager.do_resize();
141 IPython.layout_manager.do_resize();
144 });
142 });
145 this.element.find('#toggle_toolbar').click(function () {
143 this.element.find('#toggle_toolbar').click(function () {
146 IPython.toolbar.toggle();
144 $('div#maintoolbar').toggle();
145 IPython.layout_manager.do_resize();
147 });
146 });
148 // Insert
147 // Insert
149 this.element.find('#insert_cell_above').click(function () {
148 this.element.find('#insert_cell_above').click(function () {
150 IPython.notebook.insert_cell_above('code');
149 IPython.notebook.insert_cell_above('code');
151 });
150 });
152 this.element.find('#insert_cell_below').click(function () {
151 this.element.find('#insert_cell_below').click(function () {
153 IPython.notebook.insert_cell_below('code');
152 IPython.notebook.insert_cell_below('code');
154 });
153 });
155 // Cell
154 // Cell
156 this.element.find('#run_cell').click(function () {
155 this.element.find('#run_cell').click(function () {
157 IPython.notebook.execute_selected_cell();
156 IPython.notebook.execute_selected_cell();
158 });
157 });
159 this.element.find('#run_cell_in_place').click(function () {
158 this.element.find('#run_cell_in_place').click(function () {
160 IPython.notebook.execute_selected_cell({terminal:true});
159 IPython.notebook.execute_selected_cell({terminal:true});
161 });
160 });
162 this.element.find('#run_all_cells').click(function () {
161 this.element.find('#run_all_cells').click(function () {
163 IPython.notebook.execute_all_cells();
162 IPython.notebook.execute_all_cells();
164 }).attr('title', 'Run all cells in the notebook');
163 }).attr('title', 'Run all cells in the notebook');
165 this.element.find('#run_all_cells_above').click(function () {
164 this.element.find('#run_all_cells_above').click(function () {
166 IPython.notebook.execute_cells_above();
165 IPython.notebook.execute_cells_above();
167 }).attr('title', 'Run all cells above (but not including) this cell');
166 }).attr('title', 'Run all cells above (but not including) this cell');
168 this.element.find('#run_all_cells_below').click(function () {
167 this.element.find('#run_all_cells_below').click(function () {
169 IPython.notebook.execute_cells_below();
168 IPython.notebook.execute_cells_below();
170 }).attr('title', 'Run this cell and all cells below it');
169 }).attr('title', 'Run this cell and all cells below it');
171 this.element.find('#to_code').click(function () {
170 this.element.find('#to_code').click(function () {
172 IPython.notebook.to_code();
171 IPython.notebook.to_code();
173 });
172 });
174 this.element.find('#to_markdown').click(function () {
173 this.element.find('#to_markdown').click(function () {
175 IPython.notebook.to_markdown();
174 IPython.notebook.to_markdown();
176 });
175 });
177 this.element.find('#to_raw').click(function () {
176 this.element.find('#to_raw').click(function () {
178 IPython.notebook.to_raw();
177 IPython.notebook.to_raw();
179 });
178 });
180 this.element.find('#to_heading1').click(function () {
179 this.element.find('#to_heading1').click(function () {
181 IPython.notebook.to_heading(undefined, 1);
180 IPython.notebook.to_heading(undefined, 1);
182 });
181 });
183 this.element.find('#to_heading2').click(function () {
182 this.element.find('#to_heading2').click(function () {
184 IPython.notebook.to_heading(undefined, 2);
183 IPython.notebook.to_heading(undefined, 2);
185 });
184 });
186 this.element.find('#to_heading3').click(function () {
185 this.element.find('#to_heading3').click(function () {
187 IPython.notebook.to_heading(undefined, 3);
186 IPython.notebook.to_heading(undefined, 3);
188 });
187 });
189 this.element.find('#to_heading4').click(function () {
188 this.element.find('#to_heading4').click(function () {
190 IPython.notebook.to_heading(undefined, 4);
189 IPython.notebook.to_heading(undefined, 4);
191 });
190 });
192 this.element.find('#to_heading5').click(function () {
191 this.element.find('#to_heading5').click(function () {
193 IPython.notebook.to_heading(undefined, 5);
192 IPython.notebook.to_heading(undefined, 5);
194 });
193 });
195 this.element.find('#to_heading6').click(function () {
194 this.element.find('#to_heading6').click(function () {
196 IPython.notebook.to_heading(undefined, 6);
195 IPython.notebook.to_heading(undefined, 6);
197 });
196 });
198 this.element.find('#toggle_output').click(function () {
197 this.element.find('#toggle_output').click(function () {
199 IPython.notebook.toggle_output();
198 IPython.notebook.toggle_output();
200 });
199 });
201 this.element.find('#collapse_all_output').click(function () {
200 this.element.find('#collapse_all_output').click(function () {
202 IPython.notebook.collapse_all_output();
201 IPython.notebook.collapse_all_output();
203 });
202 });
204 this.element.find('#scroll_all_output').click(function () {
203 this.element.find('#scroll_all_output').click(function () {
205 IPython.notebook.scroll_all_output();
204 IPython.notebook.scroll_all_output();
206 });
205 });
207 this.element.find('#expand_all_output').click(function () {
206 this.element.find('#expand_all_output').click(function () {
208 IPython.notebook.expand_all_output();
207 IPython.notebook.expand_all_output();
209 });
208 });
210 this.element.find('#clear_all_output').click(function () {
209 this.element.find('#clear_all_output').click(function () {
211 IPython.notebook.clear_all_output();
210 IPython.notebook.clear_all_output();
212 });
211 });
213 // Kernel
212 // Kernel
214 this.element.find('#int_kernel').click(function () {
213 this.element.find('#int_kernel').click(function () {
215 IPython.notebook.kernel.interrupt();
214 IPython.notebook.kernel.interrupt();
216 });
215 });
217 this.element.find('#restart_kernel').click(function () {
216 this.element.find('#restart_kernel').click(function () {
218 IPython.notebook.restart_kernel();
217 IPython.notebook.restart_kernel();
219 });
218 });
220 // Help
219 // Help
221 this.element.find('#keyboard_shortcuts').click(function () {
220 this.element.find('#keyboard_shortcuts').click(function () {
222 IPython.quick_help.show_keyboard_shortcuts();
221 IPython.quick_help.show_keyboard_shortcuts();
223 });
222 });
224
223
225 this.update_restore_checkpoint(null);
224 this.update_restore_checkpoint(null);
226
225
227 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
226 $([IPython.events]).on('checkpoints_listed.Notebook', function (event, data) {
228 that.update_restore_checkpoint(data);
227 that.update_restore_checkpoint(data);
229 });
228 });
230
229
231 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
230 $([IPython.events]).on('checkpoint_created.Notebook', function (event, data) {
232 that.update_restore_checkpoint([data]);
231 that.update_restore_checkpoint([data]);
233 });
232 });
234 };
233 };
235
234
236 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
235 MenuBar.prototype.update_restore_checkpoint = function(checkpoints) {
237 if (! checkpoints) {
236 if (! checkpoints) {
238 checkpoints = [];
237 checkpoints = [];
239 };
238 };
240 this.element.find("#restore_checkpoint").find("ul").find("li").each(function(i) {
239 this.element.find("#restore_checkpoint").find("ul").find("li").each(function(i) {
241 var li = $(this);
240 var li = $(this);
242 var a = li.find("a");
241 var a = li.find("a");
243 a.off("click");
242 a.off("click");
244 if (checkpoints.length <= i) {
243 if (checkpoints.length <= i) {
245 li.hide();
244 li.hide();
246 return;
245 return;
247 } else {
246 } else {
248 li.show();
247 li.show();
249 };
248 };
250 var checkpoint = checkpoints[i];
249 var checkpoint = checkpoints[i];
251 var d = new Date(checkpoint.last_modified);
250 var d = new Date(checkpoint.last_modified);
252 li.find('a').text(
251 li.find('a').text(
253 d.format("mmm dd HH:MM:ss")
252 d.format("mmm dd HH:MM:ss")
254 ).click(function () {
253 ).click(function () {
255 IPython.notebook.restore_checkpoint_dialog(checkpoint);
254 IPython.notebook.restore_checkpoint_dialog(checkpoint);
256 });
255 });
257 });
256 });
258 };
257 };
259
258
260 IPython.MenuBar = MenuBar;
259 IPython.MenuBar = MenuBar;
261
260
262 return IPython;
261 return IPython;
263
262
264 }(IPython));
263 }(IPython));
@@ -1,2046 +1,2010 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // Notebook
9 // Notebook
10 //============================================================================
10 //============================================================================
11
11
12 var IPython = (function (IPython) {
12 var IPython = (function (IPython) {
13
13
14 var utils = IPython.utils;
14 var utils = IPython.utils;
15 var key = IPython.utils.keycodes;
15 var key = IPython.utils.keycodes;
16
16
17 /**
17 /**
18 * A notebook contains and manages cells.
18 * A notebook contains and manages cells.
19 *
19 *
20 * @class Notebook
20 * @class Notebook
21 * @constructor
21 * @constructor
22 * @param {String} selector A jQuery selector for the notebook's DOM element
22 * @param {String} selector A jQuery selector for the notebook's DOM element
23 * @param {Object} [options] A config object
23 * @param {Object} [options] A config object
24 */
24 */
25 var Notebook = function (selector, options) {
25 var Notebook = function (selector, options) {
26 var options = options || {};
26 var options = options || {};
27 this._baseProjectUrl = options.baseProjectUrl;
27 this._baseProjectUrl = options.baseProjectUrl;
28 this.read_only = options.read_only || IPython.read_only;
28 this.read_only = options.read_only || IPython.read_only;
29
29
30 this.element = $(selector);
30 this.element = $(selector);
31 this.element.scroll();
31 this.element.scroll();
32 this.element.data("notebook", this);
32 this.element.data("notebook", this);
33 this.next_prompt_number = 1;
33 this.next_prompt_number = 1;
34 this.kernel = null;
34 this.kernel = null;
35 this.clipboard = null;
35 this.clipboard = null;
36 this.undelete_backup = null;
36 this.undelete_backup = null;
37 this.undelete_index = null;
37 this.undelete_index = null;
38 this.undelete_below = false;
38 this.undelete_below = false;
39 this.paste_enabled = false;
39 this.paste_enabled = false;
40 this.set_dirty(false);
40 this.set_dirty(false);
41 this.metadata = {};
41 this.metadata = {};
42 this._checkpoint_after_save = false;
42 this._checkpoint_after_save = false;
43 this.last_checkpoint = null;
43 this.last_checkpoint = null;
44 this.autosave_interval = 0;
44 this.autosave_interval = 0;
45 this.autosave_timer = null;
45 this.autosave_timer = null;
46 // autosave *at most* every two minutes
46 // autosave *at most* every two minutes
47 this.minimum_autosave_interval = 120000;
47 this.minimum_autosave_interval = 120000;
48 // single worksheet for now
48 // single worksheet for now
49 this.worksheet_metadata = {};
49 this.worksheet_metadata = {};
50 this.control_key_active = false;
50 this.control_key_active = false;
51 this.notebook_id = null;
51 this.notebook_id = null;
52 this.notebook_name = null;
52 this.notebook_name = null;
53 this.notebook_name_blacklist_re = /[\/\\:]/;
53 this.notebook_name_blacklist_re = /[\/\\:]/;
54 this.nbformat = 3 // Increment this when changing the nbformat
54 this.nbformat = 3 // Increment this when changing the nbformat
55 this.nbformat_minor = 0 // Increment this when changing the nbformat
55 this.nbformat_minor = 0 // Increment this when changing the nbformat
56 this.style();
56 this.style();
57 this.create_elements();
57 this.create_elements();
58 this.bind_events();
58 this.bind_events();
59 };
59 };
60
60
61 /**
61 /**
62 * Tweak the notebook's CSS style.
62 * Tweak the notebook's CSS style.
63 *
63 *
64 * @method style
64 * @method style
65 */
65 */
66 Notebook.prototype.style = function () {
66 Notebook.prototype.style = function () {
67 $('div#notebook').addClass('border-box-sizing');
67 $('div#notebook').addClass('border-box-sizing');
68 };
68 };
69
69
70 /**
70 /**
71 * Get the root URL of the notebook server.
71 * Get the root URL of the notebook server.
72 *
72 *
73 * @method baseProjectUrl
73 * @method baseProjectUrl
74 * @return {String} The base project URL
74 * @return {String} The base project URL
75 */
75 */
76 Notebook.prototype.baseProjectUrl = function(){
76 Notebook.prototype.baseProjectUrl = function(){
77 return this._baseProjectUrl || $('body').data('baseProjectUrl');
77 return this._baseProjectUrl || $('body').data('baseProjectUrl');
78 };
78 };
79
79
80 /**
80 /**
81 * Create an HTML and CSS representation of the notebook.
81 * Create an HTML and CSS representation of the notebook.
82 *
82 *
83 * @method create_elements
83 * @method create_elements
84 */
84 */
85 Notebook.prototype.create_elements = function () {
85 Notebook.prototype.create_elements = function () {
86 // We add this end_space div to the end of the notebook div to:
86 // We add this end_space div to the end of the notebook div to:
87 // i) provide a margin between the last cell and the end of the notebook
87 // i) provide a margin between the last cell and the end of the notebook
88 // ii) to prevent the div from scrolling up when the last cell is being
88 // ii) to prevent the div from scrolling up when the last cell is being
89 // edited, but is too low on the page, which browsers will do automatically.
89 // edited, but is too low on the page, which browsers will do automatically.
90 var that = this;
90 var that = this;
91 var end_space = $('<div/>').addClass('end_space').height("30%");
91 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
92 var end_space = $('<div/>').addClass('end_space');
92 end_space.dblclick(function (e) {
93 end_space.dblclick(function (e) {
93 if (that.read_only) return;
94 if (that.read_only) return;
94 var ncells = that.ncells();
95 var ncells = that.ncells();
95 that.insert_cell_below('code',ncells-1);
96 that.insert_cell_below('code',ncells-1);
96 });
97 });
97 this.element.append(end_space);
98 this.element.append(this.container);
99 this.container.append(end_space);
98 $('div#notebook').addClass('border-box-sizing');
100 $('div#notebook').addClass('border-box-sizing');
99 };
101 };
100
102
101 /**
103 /**
102 * Bind JavaScript events: key presses and custom IPython events.
104 * Bind JavaScript events: key presses and custom IPython events.
103 *
105 *
104 * @method bind_events
106 * @method bind_events
105 */
107 */
106 Notebook.prototype.bind_events = function () {
108 Notebook.prototype.bind_events = function () {
107 var that = this;
109 var that = this;
108
110
109 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
111 $([IPython.events]).on('set_next_input.Notebook', function (event, data) {
110 var index = that.find_cell_index(data.cell);
112 var index = that.find_cell_index(data.cell);
111 var new_cell = that.insert_cell_below('code',index);
113 var new_cell = that.insert_cell_below('code',index);
112 new_cell.set_text(data.text);
114 new_cell.set_text(data.text);
113 that.dirty = true;
115 that.dirty = true;
114 });
116 });
115
117
116 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
118 $([IPython.events]).on('set_dirty.Notebook', function (event, data) {
117 that.dirty = data.value;
119 that.dirty = data.value;
118 });
120 });
119
121
120 $([IPython.events]).on('select.Cell', function (event, data) {
122 $([IPython.events]).on('select.Cell', function (event, data) {
121 var index = that.find_cell_index(data.cell);
123 var index = that.find_cell_index(data.cell);
122 that.select(index);
124 that.select(index);
123 });
125 });
124
126
125
127
126 $(document).keydown(function (event) {
128 $(document).keydown(function (event) {
127 // console.log(event);
129 // console.log(event);
128 if (that.read_only) return true;
130 if (that.read_only) return true;
129
131
130 // Save (CTRL+S) or (AppleKey+S)
132 // Save (CTRL+S) or (AppleKey+S)
131 //metaKey = applekey on mac
133 //metaKey = applekey on mac
132 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
134 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
133 that.save_checkpoint();
135 that.save_checkpoint();
134 event.preventDefault();
136 event.preventDefault();
135 return false;
137 return false;
136 } else if (event.which === key.ESC) {
138 } else if (event.which === key.ESC) {
137 // Intercept escape at highest level to avoid closing
139 // Intercept escape at highest level to avoid closing
138 // websocket connection with firefox
140 // websocket connection with firefox
139 IPython.pager.collapse();
141 IPython.pager.collapse();
140 event.preventDefault();
142 event.preventDefault();
141 } else if (event.which === key.SHIFT) {
143 } else if (event.which === key.SHIFT) {
142 // ignore shift keydown
144 // ignore shift keydown
143 return true;
145 return true;
144 }
146 }
145 if (event.which === key.UPARROW && !event.shiftKey) {
147 if (event.which === key.UPARROW && !event.shiftKey) {
146 var cell = that.get_selected_cell();
148 var cell = that.get_selected_cell();
147 if (cell && cell.at_top()) {
149 if (cell && cell.at_top()) {
148 event.preventDefault();
150 event.preventDefault();
149 that.select_prev();
151 that.select_prev();
150 };
152 };
151 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
153 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
152 var cell = that.get_selected_cell();
154 var cell = that.get_selected_cell();
153 if (cell && cell.at_bottom()) {
155 if (cell && cell.at_bottom()) {
154 event.preventDefault();
156 event.preventDefault();
155 that.select_next();
157 that.select_next();
156 };
158 };
157 } else if (event.which === key.ENTER && event.shiftKey) {
159 } else if (event.which === key.ENTER && event.shiftKey) {
158 that.execute_selected_cell();
160 that.execute_selected_cell();
159 return false;
161 return false;
160 } else if (event.which === key.ENTER && event.altKey) {
162 } else if (event.which === key.ENTER && event.altKey) {
161 // Execute code cell, and insert new in place
163 // Execute code cell, and insert new in place
162 that.execute_selected_cell();
164 that.execute_selected_cell();
163 // Only insert a new cell, if we ended up in an already populated cell
165 // Only insert a new cell, if we ended up in an already populated cell
164 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
166 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
165 that.insert_cell_above('code');
167 that.insert_cell_above('code');
166 }
168 }
167 return false;
169 return false;
168 } else if (event.which === key.ENTER && event.ctrlKey) {
170 } else if (event.which === key.ENTER && event.ctrlKey) {
169 that.execute_selected_cell({terminal:true});
171 that.execute_selected_cell({terminal:true});
170 return false;
172 return false;
171 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
173 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
172 that.control_key_active = true;
174 that.control_key_active = true;
173 return false;
175 return false;
174 } else if (event.which === 88 && that.control_key_active) {
176 } else if (event.which === 88 && that.control_key_active) {
175 // Cut selected cell = x
177 // Cut selected cell = x
176 that.cut_cell();
178 that.cut_cell();
177 that.control_key_active = false;
179 that.control_key_active = false;
178 return false;
180 return false;
179 } else if (event.which === 67 && that.control_key_active) {
181 } else if (event.which === 67 && that.control_key_active) {
180 // Copy selected cell = c
182 // Copy selected cell = c
181 that.copy_cell();
183 that.copy_cell();
182 that.control_key_active = false;
184 that.control_key_active = false;
183 return false;
185 return false;
184 } else if (event.which === 86 && that.control_key_active) {
186 } else if (event.which === 86 && that.control_key_active) {
185 // Paste below selected cell = v
187 // Paste below selected cell = v
186 that.paste_cell_below();
188 that.paste_cell_below();
187 that.control_key_active = false;
189 that.control_key_active = false;
188 return false;
190 return false;
189 } else if (event.which === 68 && that.control_key_active) {
191 } else if (event.which === 68 && that.control_key_active) {
190 // Delete selected cell = d
192 // Delete selected cell = d
191 that.delete_cell();
193 that.delete_cell();
192 that.control_key_active = false;
194 that.control_key_active = false;
193 return false;
195 return false;
194 } else if (event.which === 65 && that.control_key_active) {
196 } else if (event.which === 65 && that.control_key_active) {
195 // Insert code cell above selected = a
197 // Insert code cell above selected = a
196 that.insert_cell_above('code');
198 that.insert_cell_above('code');
197 that.control_key_active = false;
199 that.control_key_active = false;
198 return false;
200 return false;
199 } else if (event.which === 66 && that.control_key_active) {
201 } else if (event.which === 66 && that.control_key_active) {
200 // Insert code cell below selected = b
202 // Insert code cell below selected = b
201 that.insert_cell_below('code');
203 that.insert_cell_below('code');
202 that.control_key_active = false;
204 that.control_key_active = false;
203 return false;
205 return false;
204 } else if (event.which === 89 && that.control_key_active) {
206 } else if (event.which === 89 && that.control_key_active) {
205 // To code = y
207 // To code = y
206 that.to_code();
208 that.to_code();
207 that.control_key_active = false;
209 that.control_key_active = false;
208 return false;
210 return false;
209 } else if (event.which === 77 && that.control_key_active) {
211 } else if (event.which === 77 && that.control_key_active) {
210 // To markdown = m
212 // To markdown = m
211 that.to_markdown();
213 that.to_markdown();
212 that.control_key_active = false;
214 that.control_key_active = false;
213 return false;
215 return false;
214 } else if (event.which === 84 && that.control_key_active) {
216 } else if (event.which === 84 && that.control_key_active) {
215 // To Raw = t
217 // To Raw = t
216 that.to_raw();
218 that.to_raw();
217 that.control_key_active = false;
219 that.control_key_active = false;
218 return false;
220 return false;
219 } else if (event.which === 49 && that.control_key_active) {
221 } else if (event.which === 49 && that.control_key_active) {
220 // To Heading 1 = 1
222 // To Heading 1 = 1
221 that.to_heading(undefined, 1);
223 that.to_heading(undefined, 1);
222 that.control_key_active = false;
224 that.control_key_active = false;
223 return false;
225 return false;
224 } else if (event.which === 50 && that.control_key_active) {
226 } else if (event.which === 50 && that.control_key_active) {
225 // To Heading 2 = 2
227 // To Heading 2 = 2
226 that.to_heading(undefined, 2);
228 that.to_heading(undefined, 2);
227 that.control_key_active = false;
229 that.control_key_active = false;
228 return false;
230 return false;
229 } else if (event.which === 51 && that.control_key_active) {
231 } else if (event.which === 51 && that.control_key_active) {
230 // To Heading 3 = 3
232 // To Heading 3 = 3
231 that.to_heading(undefined, 3);
233 that.to_heading(undefined, 3);
232 that.control_key_active = false;
234 that.control_key_active = false;
233 return false;
235 return false;
234 } else if (event.which === 52 && that.control_key_active) {
236 } else if (event.which === 52 && that.control_key_active) {
235 // To Heading 4 = 4
237 // To Heading 4 = 4
236 that.to_heading(undefined, 4);
238 that.to_heading(undefined, 4);
237 that.control_key_active = false;
239 that.control_key_active = false;
238 return false;
240 return false;
239 } else if (event.which === 53 && that.control_key_active) {
241 } else if (event.which === 53 && that.control_key_active) {
240 // To Heading 5 = 5
242 // To Heading 5 = 5
241 that.to_heading(undefined, 5);
243 that.to_heading(undefined, 5);
242 that.control_key_active = false;
244 that.control_key_active = false;
243 return false;
245 return false;
244 } else if (event.which === 54 && that.control_key_active) {
246 } else if (event.which === 54 && that.control_key_active) {
245 // To Heading 6 = 6
247 // To Heading 6 = 6
246 that.to_heading(undefined, 6);
248 that.to_heading(undefined, 6);
247 that.control_key_active = false;
249 that.control_key_active = false;
248 return false;
250 return false;
249 } else if (event.which === 79 && that.control_key_active) {
251 } else if (event.which === 79 && that.control_key_active) {
250 // Toggle output = o
252 // Toggle output = o
251 if (event.shiftKey){
253 if (event.shiftKey){
252 that.toggle_output_scroll();
254 that.toggle_output_scroll();
253 } else {
255 } else {
254 that.toggle_output();
256 that.toggle_output();
255 }
257 }
256 that.control_key_active = false;
258 that.control_key_active = false;
257 return false;
259 return false;
258 } else if (event.which === 83 && that.control_key_active) {
260 } else if (event.which === 83 && that.control_key_active) {
259 // Save notebook = s
261 // Save notebook = s
260 that.save_checkpoint();
262 that.save_checkpoint();
261 that.control_key_active = false;
263 that.control_key_active = false;
262 return false;
264 return false;
263 } else if (event.which === 74 && that.control_key_active) {
265 } else if (event.which === 74 && that.control_key_active) {
264 // Move cell down = j
266 // Move cell down = j
265 that.move_cell_down();
267 that.move_cell_down();
266 that.control_key_active = false;
268 that.control_key_active = false;
267 return false;
269 return false;
268 } else if (event.which === 75 && that.control_key_active) {
270 } else if (event.which === 75 && that.control_key_active) {
269 // Move cell up = k
271 // Move cell up = k
270 that.move_cell_up();
272 that.move_cell_up();
271 that.control_key_active = false;
273 that.control_key_active = false;
272 return false;
274 return false;
273 } else if (event.which === 80 && that.control_key_active) {
275 } else if (event.which === 80 && that.control_key_active) {
274 // Select previous = p
276 // Select previous = p
275 that.select_prev();
277 that.select_prev();
276 that.control_key_active = false;
278 that.control_key_active = false;
277 return false;
279 return false;
278 } else if (event.which === 78 && that.control_key_active) {
280 } else if (event.which === 78 && that.control_key_active) {
279 // Select next = n
281 // Select next = n
280 that.select_next();
282 that.select_next();
281 that.control_key_active = false;
283 that.control_key_active = false;
282 return false;
284 return false;
283 } else if (event.which === 76 && that.control_key_active) {
285 } else if (event.which === 76 && that.control_key_active) {
284 // Toggle line numbers = l
286 // Toggle line numbers = l
285 that.cell_toggle_line_numbers();
287 that.cell_toggle_line_numbers();
286 that.control_key_active = false;
288 that.control_key_active = false;
287 return false;
289 return false;
288 } else if (event.which === 73 && that.control_key_active) {
290 } else if (event.which === 73 && that.control_key_active) {
289 // Interrupt kernel = i
291 // Interrupt kernel = i
290 that.kernel.interrupt();
292 that.kernel.interrupt();
291 that.control_key_active = false;
293 that.control_key_active = false;
292 return false;
294 return false;
293 } else if (event.which === 190 && that.control_key_active) {
295 } else if (event.which === 190 && that.control_key_active) {
294 // Restart kernel = . # matches qt console
296 // Restart kernel = . # matches qt console
295 that.restart_kernel();
297 that.restart_kernel();
296 that.control_key_active = false;
298 that.control_key_active = false;
297 return false;
299 return false;
298 } else if (event.which === 72 && that.control_key_active) {
300 } else if (event.which === 72 && that.control_key_active) {
299 // Show keyboard shortcuts = h
301 // Show keyboard shortcuts = h
300 IPython.quick_help.show_keyboard_shortcuts();
302 IPython.quick_help.show_keyboard_shortcuts();
301 that.control_key_active = false;
303 that.control_key_active = false;
302 return false;
304 return false;
303 } else if (event.which === 90 && that.control_key_active) {
305 } else if (event.which === 90 && that.control_key_active) {
304 // Undo last cell delete = z
306 // Undo last cell delete = z
305 that.undelete();
307 that.undelete();
306 that.control_key_active = false;
308 that.control_key_active = false;
307 return false;
309 return false;
308 } else if (that.control_key_active) {
310 } else if (that.control_key_active) {
309 that.control_key_active = false;
311 that.control_key_active = false;
310 return true;
312 return true;
311 }
313 }
312 return true;
314 return true;
313 });
315 });
314
316
315 var collapse_time = function(time){
317 var collapse_time = function(time){
316 var app_height = $('#ipython-main-app').height(); // content height
318 var app_height = $('#ipython-main-app').height(); // content height
317 var splitter_height = $('div#pager_splitter').outerHeight(true);
319 var splitter_height = $('div#pager_splitter').outerHeight(true);
318 var new_height = app_height - splitter_height;
320 var new_height = app_height - splitter_height;
319 that.element.animate({height : new_height + 'px'}, time);
321 that.element.animate({height : new_height + 'px'}, time);
320 }
322 }
321
323
322 this.element.bind('collapse_pager', function (event,extrap) {
324 this.element.bind('collapse_pager', function (event,extrap) {
323 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
325 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
324 collapse_time(time);
326 collapse_time(time);
325 });
327 });
326
328
327 var expand_time = function(time) {
329 var expand_time = function(time) {
328 var app_height = $('#ipython-main-app').height(); // content height
330 var app_height = $('#ipython-main-app').height(); // content height
329 var splitter_height = $('div#pager_splitter').outerHeight(true);
331 var splitter_height = $('div#pager_splitter').outerHeight(true);
330 var pager_height = $('div#pager').outerHeight(true);
332 var pager_height = $('div#pager').outerHeight(true);
331 var new_height = app_height - pager_height - splitter_height;
333 var new_height = app_height - pager_height - splitter_height;
332 that.element.animate({height : new_height + 'px'}, time);
334 that.element.animate({height : new_height + 'px'}, time);
333 }
335 }
334
336
335 this.element.bind('expand_pager', function (event, extrap) {
337 this.element.bind('expand_pager', function (event, extrap) {
336 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
338 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
337 expand_time(time);
339 expand_time(time);
338 });
340 });
339
341
340 $(window).bind('beforeunload', function () {
342 $(window).bind('beforeunload', function () {
341 // TODO: Make killing the kernel configurable.
343 // TODO: Make killing the kernel configurable.
342 var kill_kernel = false;
344 var kill_kernel = false;
343 if (kill_kernel) {
345 if (kill_kernel) {
344 that.kernel.kill();
346 that.kernel.kill();
345 }
347 }
346 // if we are autosaving, trigger an autosave on nav-away
348 // if we are autosaving, trigger an autosave on nav-away
347 if (that.dirty && that.autosave_interval && ! that.read_only) {
349 if (that.dirty && that.autosave_interval && ! that.read_only) {
348 that.save_notebook();
350 that.save_notebook();
349 };
351 };
350 // Null is the *only* return value that will make the browser not
352 // Null is the *only* return value that will make the browser not
351 // pop up the "don't leave" dialog.
353 // pop up the "don't leave" dialog.
352 return null;
354 return null;
353 });
355 });
354 };
356 };
355
357
356 /**
358 /**
357 * Set the dirty flag, and trigger the set_dirty.Notebook event
359 * Set the dirty flag, and trigger the set_dirty.Notebook event
358 *
360 *
359 * @method set_dirty
361 * @method set_dirty
360 */
362 */
361 Notebook.prototype.set_dirty = function (value) {
363 Notebook.prototype.set_dirty = function (value) {
362 if (value === undefined) {
364 if (value === undefined) {
363 value = true;
365 value = true;
364 }
366 }
365 if (this.dirty == value) {
367 if (this.dirty == value) {
366 return;
368 return;
367 }
369 }
368 $([IPython.events]).trigger('set_dirty.Notebook', {value: value});
370 $([IPython.events]).trigger('set_dirty.Notebook', {value: value});
369 };
371 };
370
372
371 /**
373 /**
372 * Scroll the top of the page to a given cell.
374 * Scroll the top of the page to a given cell.
373 *
375 *
374 * @method scroll_to_cell
376 * @method scroll_to_cell
375 * @param {Number} cell_number An index of the cell to view
377 * @param {Number} cell_number An index of the cell to view
376 * @param {Number} time Animation time in milliseconds
378 * @param {Number} time Animation time in milliseconds
377 * @return {Number} Pixel offset from the top of the container
379 * @return {Number} Pixel offset from the top of the container
378 */
380 */
379 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
381 Notebook.prototype.scroll_to_cell = function (cell_number, time) {
380 var cells = this.get_cells();
382 var cells = this.get_cells();
381 var time = time || 0;
383 var time = time || 0;
382 cell_number = Math.min(cells.length-1,cell_number);
384 cell_number = Math.min(cells.length-1,cell_number);
383 cell_number = Math.max(0 ,cell_number);
385 cell_number = Math.max(0 ,cell_number);
384 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
386 var scroll_value = cells[cell_number].element.position().top-cells[0].element.position().top ;
385 this.element.animate({scrollTop:scroll_value}, time);
387 this.element.animate({scrollTop:scroll_value}, time);
386 return scroll_value;
388 return scroll_value;
387 };
389 };
388
390
389 /**
391 /**
390 * Scroll to the bottom of the page.
392 * Scroll to the bottom of the page.
391 *
393 *
392 * @method scroll_to_bottom
394 * @method scroll_to_bottom
393 */
395 */
394 Notebook.prototype.scroll_to_bottom = function () {
396 Notebook.prototype.scroll_to_bottom = function () {
395 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
397 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
396 };
398 };
397
399
398 /**
400 /**
399 * Scroll to the top of the page.
401 * Scroll to the top of the page.
400 *
402 *
401 * @method scroll_to_top
403 * @method scroll_to_top
402 */
404 */
403 Notebook.prototype.scroll_to_top = function () {
405 Notebook.prototype.scroll_to_top = function () {
404 this.element.animate({scrollTop:0}, 0);
406 this.element.animate({scrollTop:0}, 0);
405 };
407 };
406
408
407
409
408 // Cell indexing, retrieval, etc.
410 // Cell indexing, retrieval, etc.
409
411
410 /**
412 /**
411 * Get all cell elements in the notebook.
413 * Get all cell elements in the notebook.
412 *
414 *
413 * @method get_cell_elements
415 * @method get_cell_elements
414 * @return {jQuery} A selector of all cell elements
416 * @return {jQuery} A selector of all cell elements
415 */
417 */
416 Notebook.prototype.get_cell_elements = function () {
418 Notebook.prototype.get_cell_elements = function () {
417 return this.element.children("div.cell");
419 return this.container.children("div.cell");
418 };
420 };
419
421
420 /**
422 /**
421 * Get a particular cell element.
423 * Get a particular cell element.
422 *
424 *
423 * @method get_cell_element
425 * @method get_cell_element
424 * @param {Number} index An index of a cell to select
426 * @param {Number} index An index of a cell to select
425 * @return {jQuery} A selector of the given cell.
427 * @return {jQuery} A selector of the given cell.
426 */
428 */
427 Notebook.prototype.get_cell_element = function (index) {
429 Notebook.prototype.get_cell_element = function (index) {
428 var result = null;
430 var result = null;
429 var e = this.get_cell_elements().eq(index);
431 var e = this.get_cell_elements().eq(index);
430 if (e.length !== 0) {
432 if (e.length !== 0) {
431 result = e;
433 result = e;
432 }
434 }
433 return result;
435 return result;
434 };
436 };
435
437
436 /**
438 /**
437 * Count the cells in this notebook.
439 * Count the cells in this notebook.
438 *
440 *
439 * @method ncells
441 * @method ncells
440 * @return {Number} The number of cells in this notebook
442 * @return {Number} The number of cells in this notebook
441 */
443 */
442 Notebook.prototype.ncells = function () {
444 Notebook.prototype.ncells = function () {
443 return this.get_cell_elements().length;
445 return this.get_cell_elements().length;
444 };
446 };
445
447
446 /**
448 /**
447 * Get all Cell objects in this notebook.
449 * Get all Cell objects in this notebook.
448 *
450 *
449 * @method get_cells
451 * @method get_cells
450 * @return {Array} This notebook's Cell objects
452 * @return {Array} This notebook's Cell objects
451 */
453 */
452 // TODO: we are often calling cells as cells()[i], which we should optimize
454 // TODO: we are often calling cells as cells()[i], which we should optimize
453 // to cells(i) or a new method.
455 // to cells(i) or a new method.
454 Notebook.prototype.get_cells = function () {
456 Notebook.prototype.get_cells = function () {
455 return this.get_cell_elements().toArray().map(function (e) {
457 return this.get_cell_elements().toArray().map(function (e) {
456 return $(e).data("cell");
458 return $(e).data("cell");
457 });
459 });
458 };
460 };
459
461
460 /**
462 /**
461 * Get a Cell object from this notebook.
463 * Get a Cell object from this notebook.
462 *
464 *
463 * @method get_cell
465 * @method get_cell
464 * @param {Number} index An index of a cell to retrieve
466 * @param {Number} index An index of a cell to retrieve
465 * @return {Cell} A particular cell
467 * @return {Cell} A particular cell
466 */
468 */
467 Notebook.prototype.get_cell = function (index) {
469 Notebook.prototype.get_cell = function (index) {
468 var result = null;
470 var result = null;
469 var ce = this.get_cell_element(index);
471 var ce = this.get_cell_element(index);
470 if (ce !== null) {
472 if (ce !== null) {
471 result = ce.data('cell');
473 result = ce.data('cell');
472 }
474 }
473 return result;
475 return result;
474 }
476 }
475
477
476 /**
478 /**
477 * Get the cell below a given cell.
479 * Get the cell below a given cell.
478 *
480 *
479 * @method get_next_cell
481 * @method get_next_cell
480 * @param {Cell} cell The provided cell
482 * @param {Cell} cell The provided cell
481 * @return {Cell} The next cell
483 * @return {Cell} The next cell
482 */
484 */
483 Notebook.prototype.get_next_cell = function (cell) {
485 Notebook.prototype.get_next_cell = function (cell) {
484 var result = null;
486 var result = null;
485 var index = this.find_cell_index(cell);
487 var index = this.find_cell_index(cell);
486 if (this.is_valid_cell_index(index+1)) {
488 if (this.is_valid_cell_index(index+1)) {
487 result = this.get_cell(index+1);
489 result = this.get_cell(index+1);
488 }
490 }
489 return result;
491 return result;
490 }
492 }
491
493
492 /**
494 /**
493 * Get the cell above a given cell.
495 * Get the cell above a given cell.
494 *
496 *
495 * @method get_prev_cell
497 * @method get_prev_cell
496 * @param {Cell} cell The provided cell
498 * @param {Cell} cell The provided cell
497 * @return {Cell} The previous cell
499 * @return {Cell} The previous cell
498 */
500 */
499 Notebook.prototype.get_prev_cell = function (cell) {
501 Notebook.prototype.get_prev_cell = function (cell) {
500 // TODO: off-by-one
502 // TODO: off-by-one
501 // nb.get_prev_cell(nb.get_cell(1)) is null
503 // nb.get_prev_cell(nb.get_cell(1)) is null
502 var result = null;
504 var result = null;
503 var index = this.find_cell_index(cell);
505 var index = this.find_cell_index(cell);
504 if (index !== null && index > 1) {
506 if (index !== null && index > 1) {
505 result = this.get_cell(index-1);
507 result = this.get_cell(index-1);
506 }
508 }
507 return result;
509 return result;
508 }
510 }
509
511
510 /**
512 /**
511 * Get the numeric index of a given cell.
513 * Get the numeric index of a given cell.
512 *
514 *
513 * @method find_cell_index
515 * @method find_cell_index
514 * @param {Cell} cell The provided cell
516 * @param {Cell} cell The provided cell
515 * @return {Number} The cell's numeric index
517 * @return {Number} The cell's numeric index
516 */
518 */
517 Notebook.prototype.find_cell_index = function (cell) {
519 Notebook.prototype.find_cell_index = function (cell) {
518 var result = null;
520 var result = null;
519 this.get_cell_elements().filter(function (index) {
521 this.get_cell_elements().filter(function (index) {
520 if ($(this).data("cell") === cell) {
522 if ($(this).data("cell") === cell) {
521 result = index;
523 result = index;
522 };
524 };
523 });
525 });
524 return result;
526 return result;
525 };
527 };
526
528
527 /**
529 /**
528 * Get a given index , or the selected index if none is provided.
530 * Get a given index , or the selected index if none is provided.
529 *
531 *
530 * @method index_or_selected
532 * @method index_or_selected
531 * @param {Number} index A cell's index
533 * @param {Number} index A cell's index
532 * @return {Number} The given index, or selected index if none is provided.
534 * @return {Number} The given index, or selected index if none is provided.
533 */
535 */
534 Notebook.prototype.index_or_selected = function (index) {
536 Notebook.prototype.index_or_selected = function (index) {
535 var i;
537 var i;
536 if (index === undefined || index === null) {
538 if (index === undefined || index === null) {
537 i = this.get_selected_index();
539 i = this.get_selected_index();
538 if (i === null) {
540 if (i === null) {
539 i = 0;
541 i = 0;
540 }
542 }
541 } else {
543 } else {
542 i = index;
544 i = index;
543 }
545 }
544 return i;
546 return i;
545 };
547 };
546
548
547 /**
549 /**
548 * Get the currently selected cell.
550 * Get the currently selected cell.
549 * @method get_selected_cell
551 * @method get_selected_cell
550 * @return {Cell} The selected cell
552 * @return {Cell} The selected cell
551 */
553 */
552 Notebook.prototype.get_selected_cell = function () {
554 Notebook.prototype.get_selected_cell = function () {
553 var index = this.get_selected_index();
555 var index = this.get_selected_index();
554 return this.get_cell(index);
556 return this.get_cell(index);
555 };
557 };
556
558
557 /**
559 /**
558 * Check whether a cell index is valid.
560 * Check whether a cell index is valid.
559 *
561 *
560 * @method is_valid_cell_index
562 * @method is_valid_cell_index
561 * @param {Number} index A cell index
563 * @param {Number} index A cell index
562 * @return True if the index is valid, false otherwise
564 * @return True if the index is valid, false otherwise
563 */
565 */
564 Notebook.prototype.is_valid_cell_index = function (index) {
566 Notebook.prototype.is_valid_cell_index = function (index) {
565 if (index !== null && index >= 0 && index < this.ncells()) {
567 if (index !== null && index >= 0 && index < this.ncells()) {
566 return true;
568 return true;
567 } else {
569 } else {
568 return false;
570 return false;
569 };
571 };
570 }
572 }
571
573
572 /**
574 /**
573 * Get the index of the currently selected cell.
575 * Get the index of the currently selected cell.
574
576
575 * @method get_selected_index
577 * @method get_selected_index
576 * @return {Number} The selected cell's numeric index
578 * @return {Number} The selected cell's numeric index
577 */
579 */
578 Notebook.prototype.get_selected_index = function () {
580 Notebook.prototype.get_selected_index = function () {
579 var result = null;
581 var result = null;
580 this.get_cell_elements().filter(function (index) {
582 this.get_cell_elements().filter(function (index) {
581 if ($(this).data("cell").selected === true) {
583 if ($(this).data("cell").selected === true) {
582 result = index;
584 result = index;
583 };
585 };
584 });
586 });
585 return result;
587 return result;
586 };
588 };
587
589
588
590
589 // Cell selection.
591 // Cell selection.
590
592
591 /**
593 /**
592 * Programmatically select a cell.
594 * Programmatically select a cell.
593 *
595 *
594 * @method select
596 * @method select
595 * @param {Number} index A cell's index
597 * @param {Number} index A cell's index
596 * @return {Notebook} This notebook
598 * @return {Notebook} This notebook
597 */
599 */
598 Notebook.prototype.select = function (index) {
600 Notebook.prototype.select = function (index) {
599 if (this.is_valid_cell_index(index)) {
601 if (this.is_valid_cell_index(index)) {
600 var sindex = this.get_selected_index()
602 var sindex = this.get_selected_index()
601 if (sindex !== null && index !== sindex) {
603 if (sindex !== null && index !== sindex) {
602 this.get_cell(sindex).unselect();
604 this.get_cell(sindex).unselect();
603 };
605 };
604 var cell = this.get_cell(index);
606 var cell = this.get_cell(index);
605 cell.select();
607 cell.select();
606 if (cell.cell_type === 'heading') {
608 if (cell.cell_type === 'heading') {
607 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
609 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
608 {'cell_type':cell.cell_type,level:cell.level}
610 {'cell_type':cell.cell_type,level:cell.level}
609 );
611 );
610 } else {
612 } else {
611 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
613 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
612 {'cell_type':cell.cell_type}
614 {'cell_type':cell.cell_type}
613 );
615 );
614 };
616 };
615 };
617 };
616 return this;
618 return this;
617 };
619 };
618
620
619 /**
621 /**
620 * Programmatically select the next cell.
622 * Programmatically select the next cell.
621 *
623 *
622 * @method select_next
624 * @method select_next
623 * @return {Notebook} This notebook
625 * @return {Notebook} This notebook
624 */
626 */
625 Notebook.prototype.select_next = function () {
627 Notebook.prototype.select_next = function () {
626 var index = this.get_selected_index();
628 var index = this.get_selected_index();
627 this.select(index+1);
629 this.select(index+1);
628 return this;
630 return this;
629 };
631 };
630
632
631 /**
633 /**
632 * Programmatically select the previous cell.
634 * Programmatically select the previous cell.
633 *
635 *
634 * @method select_prev
636 * @method select_prev
635 * @return {Notebook} This notebook
637 * @return {Notebook} This notebook
636 */
638 */
637 Notebook.prototype.select_prev = function () {
639 Notebook.prototype.select_prev = function () {
638 var index = this.get_selected_index();
640 var index = this.get_selected_index();
639 this.select(index-1);
641 this.select(index-1);
640 return this;
642 return this;
641 };
643 };
642
644
643
645
644 // Cell movement
646 // Cell movement
645
647
646 /**
648 /**
647 * Move given (or selected) cell up and select it.
649 * Move given (or selected) cell up and select it.
648 *
650 *
649 * @method move_cell_up
651 * @method move_cell_up
650 * @param [index] {integer} cell index
652 * @param [index] {integer} cell index
651 * @return {Notebook} This notebook
653 * @return {Notebook} This notebook
652 **/
654 **/
653 Notebook.prototype.move_cell_up = function (index) {
655 Notebook.prototype.move_cell_up = function (index) {
654 var i = this.index_or_selected(index);
656 var i = this.index_or_selected(index);
655 if (this.is_valid_cell_index(i) && i > 0) {
657 if (this.is_valid_cell_index(i) && i > 0) {
656 var pivot = this.get_cell_element(i-1);
658 var pivot = this.get_cell_element(i-1);
657 var tomove = this.get_cell_element(i);
659 var tomove = this.get_cell_element(i);
658 if (pivot !== null && tomove !== null) {
660 if (pivot !== null && tomove !== null) {
659 tomove.detach();
661 tomove.detach();
660 pivot.before(tomove);
662 pivot.before(tomove);
661 this.select(i-1);
663 this.select(i-1);
662 };
664 };
663 this.set_dirty(true);
665 this.set_dirty(true);
664 };
666 };
665 return this;
667 return this;
666 };
668 };
667
669
668
670
669 /**
671 /**
670 * Move given (or selected) cell down and select it
672 * Move given (or selected) cell down and select it
671 *
673 *
672 * @method move_cell_down
674 * @method move_cell_down
673 * @param [index] {integer} cell index
675 * @param [index] {integer} cell index
674 * @return {Notebook} This notebook
676 * @return {Notebook} This notebook
675 **/
677 **/
676 Notebook.prototype.move_cell_down = function (index) {
678 Notebook.prototype.move_cell_down = function (index) {
677 var i = this.index_or_selected(index);
679 var i = this.index_or_selected(index);
678 if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
680 if ( this.is_valid_cell_index(i) && this.is_valid_cell_index(i+1)) {
679 var pivot = this.get_cell_element(i+1);
681 var pivot = this.get_cell_element(i+1);
680 var tomove = this.get_cell_element(i);
682 var tomove = this.get_cell_element(i);
681 if (pivot !== null && tomove !== null) {
683 if (pivot !== null && tomove !== null) {
682 tomove.detach();
684 tomove.detach();
683 pivot.after(tomove);
685 pivot.after(tomove);
684 this.select(i+1);
686 this.select(i+1);
685 };
687 };
686 };
688 };
687 this.set_dirty();
689 this.set_dirty();
688 return this;
690 return this;
689 };
691 };
690
692
691
693
692 // Insertion, deletion.
694 // Insertion, deletion.
693
695
694 /**
696 /**
695 * Delete a cell from the notebook.
697 * Delete a cell from the notebook.
696 *
698 *
697 * @method delete_cell
699 * @method delete_cell
698 * @param [index] A cell's numeric index
700 * @param [index] A cell's numeric index
699 * @return {Notebook} This notebook
701 * @return {Notebook} This notebook
700 */
702 */
701 Notebook.prototype.delete_cell = function (index) {
703 Notebook.prototype.delete_cell = function (index) {
702 var i = this.index_or_selected(index);
704 var i = this.index_or_selected(index);
703 var cell = this.get_selected_cell();
705 var cell = this.get_selected_cell();
704 this.undelete_backup = cell.toJSON();
706 this.undelete_backup = cell.toJSON();
705 $('#undelete_cell').removeClass('ui-state-disabled');
707 $('#undelete_cell').removeClass('ui-state-disabled');
706 if (this.is_valid_cell_index(i)) {
708 if (this.is_valid_cell_index(i)) {
707 var ce = this.get_cell_element(i);
709 var ce = this.get_cell_element(i);
708 ce.remove();
710 ce.remove();
709 if (i === (this.ncells())) {
711 if (i === (this.ncells())) {
710 this.select(i-1);
712 this.select(i-1);
711 this.undelete_index = i - 1;
713 this.undelete_index = i - 1;
712 this.undelete_below = true;
714 this.undelete_below = true;
713 } else {
715 } else {
714 this.select(i);
716 this.select(i);
715 this.undelete_index = i;
717 this.undelete_index = i;
716 this.undelete_below = false;
718 this.undelete_below = false;
717 };
719 };
718 this.set_dirty(true);
720 this.set_dirty(true);
719 };
721 };
720 return this;
722 return this;
721 };
723 };
722
724
723 /**
725 /**
724 * Insert a cell so that after insertion the cell is at given index.
726 * Insert a cell so that after insertion the cell is at given index.
725 *
727 *
726 * Similar to insert_above, but index parameter is mandatory
728 * Similar to insert_above, but index parameter is mandatory
727 *
729 *
728 * Index will be brought back into the accissible range [0,n]
730 * Index will be brought back into the accissible range [0,n]
729 *
731 *
730 * @method insert_cell_at_index
732 * @method insert_cell_at_index
731 * @param type {string} in ['code','markdown','heading']
733 * @param type {string} in ['code','markdown','heading']
732 * @param [index] {int} a valid index where to inser cell
734 * @param [index] {int} a valid index where to inser cell
733 *
735 *
734 * @return cell {cell|null} created cell or null
736 * @return cell {cell|null} created cell or null
735 **/
737 **/
736 Notebook.prototype.insert_cell_at_index = function(type, index){
738 Notebook.prototype.insert_cell_at_index = function(type, index){
737
739
738 var ncells = this.ncells();
740 var ncells = this.ncells();
739 var index = Math.min(index,ncells);
741 var index = Math.min(index,ncells);
740 index = Math.max(index,0);
742 index = Math.max(index,0);
741 var cell = null;
743 var cell = null;
742
744
743 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
745 if (ncells === 0 || this.is_valid_cell_index(index) || index === ncells) {
744 if (type === 'code') {
746 if (type === 'code') {
745 cell = new IPython.CodeCell(this.kernel);
747 cell = new IPython.CodeCell(this.kernel);
746 cell.set_input_prompt();
748 cell.set_input_prompt();
747 } else if (type === 'markdown') {
749 } else if (type === 'markdown') {
748 cell = new IPython.MarkdownCell();
750 cell = new IPython.MarkdownCell();
749 } else if (type === 'raw') {
751 } else if (type === 'raw') {
750 cell = new IPython.RawCell();
752 cell = new IPython.RawCell();
751 } else if (type === 'heading') {
753 } else if (type === 'heading') {
752 cell = new IPython.HeadingCell();
754 cell = new IPython.HeadingCell();
753 }
755 }
754
756
755 if(this._insert_element_at_index(cell.element,index)){
757 if(this._insert_element_at_index(cell.element,index)){
756 cell.render();
758 cell.render();
757 this.select(this.find_cell_index(cell));
759 this.select(this.find_cell_index(cell));
758 this.set_dirty(true);
760 this.set_dirty(true);
759 }
761 }
760 }
762 }
761 return cell;
763 return cell;
762
764
763 };
765 };
764
766
765 /**
767 /**
766 * Insert an element at given cell index.
768 * Insert an element at given cell index.
767 *
769 *
768 * @method _insert_element_at_index
770 * @method _insert_element_at_index
769 * @param element {dom element} a cell element
771 * @param element {dom element} a cell element
770 * @param [index] {int} a valid index where to inser cell
772 * @param [index] {int} a valid index where to inser cell
771 * @private
773 * @private
772 *
774 *
773 * return true if everything whent fine.
775 * return true if everything whent fine.
774 **/
776 **/
775 Notebook.prototype._insert_element_at_index = function(element, index){
777 Notebook.prototype._insert_element_at_index = function(element, index){
776 if (element === undefined){
778 if (element === undefined){
777 return false;
779 return false;
778 }
780 }
779
781
780 var ncells = this.ncells();
782 var ncells = this.ncells();
781
783
782 if (ncells === 0) {
784 if (ncells === 0) {
783 // special case append if empty
785 // special case append if empty
784 this.element.find('div.end_space').before(element);
786 this.element.find('div.end_space').before(element);
785 } else if ( ncells === index ) {
787 } else if ( ncells === index ) {
786 // special case append it the end, but not empty
788 // special case append it the end, but not empty
787 this.get_cell_element(index-1).after(element);
789 this.get_cell_element(index-1).after(element);
788 } else if (this.is_valid_cell_index(index)) {
790 } else if (this.is_valid_cell_index(index)) {
789 // otherwise always somewhere to append to
791 // otherwise always somewhere to append to
790 this.get_cell_element(index).before(element);
792 this.get_cell_element(index).before(element);
791 } else {
793 } else {
792 return false;
794 return false;
793 }
795 }
794
796
795 if (this.undelete_index !== null && index <= this.undelete_index) {
797 if (this.undelete_index !== null && index <= this.undelete_index) {
796 this.undelete_index = this.undelete_index + 1;
798 this.undelete_index = this.undelete_index + 1;
797 this.set_dirty(true);
799 this.set_dirty(true);
798 }
800 }
799 return true;
801 return true;
800 };
802 };
801
803
802 /**
804 /**
803 * Insert a cell of given type above given index, or at top
805 * Insert a cell of given type above given index, or at top
804 * of notebook if index smaller than 0.
806 * of notebook if index smaller than 0.
805 *
807 *
806 * default index value is the one of currently selected cell
808 * default index value is the one of currently selected cell
807 *
809 *
808 * @method insert_cell_above
810 * @method insert_cell_above
809 * @param type {string} cell type
811 * @param type {string} cell type
810 * @param [index] {integer}
812 * @param [index] {integer}
811 *
813 *
812 * @return handle to created cell or null
814 * @return handle to created cell or null
813 **/
815 **/
814 Notebook.prototype.insert_cell_above = function (type, index) {
816 Notebook.prototype.insert_cell_above = function (type, index) {
815 index = this.index_or_selected(index);
817 index = this.index_or_selected(index);
816 return this.insert_cell_at_index(type, index);
818 return this.insert_cell_at_index(type, index);
817 };
819 };
818
820
819 /**
821 /**
820 * Insert a cell of given type below given index, or at bottom
822 * Insert a cell of given type below given index, or at bottom
821 * of notebook if index greater thatn number of cell
823 * of notebook if index greater thatn number of cell
822 *
824 *
823 * default index value is the one of currently selected cell
825 * default index value is the one of currently selected cell
824 *
826 *
825 * @method insert_cell_below
827 * @method insert_cell_below
826 * @param type {string} cell type
828 * @param type {string} cell type
827 * @param [index] {integer}
829 * @param [index] {integer}
828 *
830 *
829 * @return handle to created cell or null
831 * @return handle to created cell or null
830 *
832 *
831 **/
833 **/
832 Notebook.prototype.insert_cell_below = function (type, index) {
834 Notebook.prototype.insert_cell_below = function (type, index) {
833 index = this.index_or_selected(index);
835 index = this.index_or_selected(index);
834 return this.insert_cell_at_index(type, index+1);
836 return this.insert_cell_at_index(type, index+1);
835 };
837 };
836
838
837
839
838 /**
840 /**
839 * Insert cell at end of notebook
841 * Insert cell at end of notebook
840 *
842 *
841 * @method insert_cell_at_bottom
843 * @method insert_cell_at_bottom
842 * @param {String} type cell type
844 * @param {String} type cell type
843 *
845 *
844 * @return the added cell; or null
846 * @return the added cell; or null
845 **/
847 **/
846 Notebook.prototype.insert_cell_at_bottom = function (type){
848 Notebook.prototype.insert_cell_at_bottom = function (type){
847 var len = this.ncells();
849 var len = this.ncells();
848 return this.insert_cell_below(type,len-1);
850 return this.insert_cell_below(type,len-1);
849 };
851 };
850
852
851 /**
853 /**
852 * Turn a cell into a code cell.
854 * Turn a cell into a code cell.
853 *
855 *
854 * @method to_code
856 * @method to_code
855 * @param {Number} [index] A cell's index
857 * @param {Number} [index] A cell's index
856 */
858 */
857 Notebook.prototype.to_code = function (index) {
859 Notebook.prototype.to_code = function (index) {
858 var i = this.index_or_selected(index);
860 var i = this.index_or_selected(index);
859 if (this.is_valid_cell_index(i)) {
861 if (this.is_valid_cell_index(i)) {
860 var source_element = this.get_cell_element(i);
862 var source_element = this.get_cell_element(i);
861 var source_cell = source_element.data("cell");
863 var source_cell = source_element.data("cell");
862 if (!(source_cell instanceof IPython.CodeCell)) {
864 if (!(source_cell instanceof IPython.CodeCell)) {
863 var target_cell = this.insert_cell_below('code',i);
865 var target_cell = this.insert_cell_below('code',i);
864 var text = source_cell.get_text();
866 var text = source_cell.get_text();
865 if (text === source_cell.placeholder) {
867 if (text === source_cell.placeholder) {
866 text = '';
868 text = '';
867 }
869 }
868 target_cell.set_text(text);
870 target_cell.set_text(text);
869 // make this value the starting point, so that we can only undo
871 // make this value the starting point, so that we can only undo
870 // to this state, instead of a blank cell
872 // to this state, instead of a blank cell
871 target_cell.code_mirror.clearHistory();
873 target_cell.code_mirror.clearHistory();
872 source_element.remove();
874 source_element.remove();
873 this.set_dirty(true);
875 this.set_dirty(true);
874 };
876 };
875 };
877 };
876 };
878 };
877
879
878 /**
880 /**
879 * Turn a cell into a Markdown cell.
881 * Turn a cell into a Markdown cell.
880 *
882 *
881 * @method to_markdown
883 * @method to_markdown
882 * @param {Number} [index] A cell's index
884 * @param {Number} [index] A cell's index
883 */
885 */
884 Notebook.prototype.to_markdown = function (index) {
886 Notebook.prototype.to_markdown = function (index) {
885 var i = this.index_or_selected(index);
887 var i = this.index_or_selected(index);
886 if (this.is_valid_cell_index(i)) {
888 if (this.is_valid_cell_index(i)) {
887 var source_element = this.get_cell_element(i);
889 var source_element = this.get_cell_element(i);
888 var source_cell = source_element.data("cell");
890 var source_cell = source_element.data("cell");
889 if (!(source_cell instanceof IPython.MarkdownCell)) {
891 if (!(source_cell instanceof IPython.MarkdownCell)) {
890 var target_cell = this.insert_cell_below('markdown',i);
892 var target_cell = this.insert_cell_below('markdown',i);
891 var text = source_cell.get_text();
893 var text = source_cell.get_text();
892 if (text === source_cell.placeholder) {
894 if (text === source_cell.placeholder) {
893 text = '';
895 text = '';
894 };
896 };
895 // The edit must come before the set_text.
897 // The edit must come before the set_text.
896 target_cell.edit();
898 target_cell.edit();
897 target_cell.set_text(text);
899 target_cell.set_text(text);
898 // make this value the starting point, so that we can only undo
900 // make this value the starting point, so that we can only undo
899 // to this state, instead of a blank cell
901 // to this state, instead of a blank cell
900 target_cell.code_mirror.clearHistory();
902 target_cell.code_mirror.clearHistory();
901 source_element.remove();
903 source_element.remove();
902 this.set_dirty(true);
904 this.set_dirty(true);
903 };
905 };
904 };
906 };
905 };
907 };
906
908
907 /**
909 /**
908 * Turn a cell into a raw text cell.
910 * Turn a cell into a raw text cell.
909 *
911 *
910 * @method to_raw
912 * @method to_raw
911 * @param {Number} [index] A cell's index
913 * @param {Number} [index] A cell's index
912 */
914 */
913 Notebook.prototype.to_raw = function (index) {
915 Notebook.prototype.to_raw = function (index) {
914 var i = this.index_or_selected(index);
916 var i = this.index_or_selected(index);
915 if (this.is_valid_cell_index(i)) {
917 if (this.is_valid_cell_index(i)) {
916 var source_element = this.get_cell_element(i);
918 var source_element = this.get_cell_element(i);
917 var source_cell = source_element.data("cell");
919 var source_cell = source_element.data("cell");
918 var target_cell = null;
920 var target_cell = null;
919 if (!(source_cell instanceof IPython.RawCell)) {
921 if (!(source_cell instanceof IPython.RawCell)) {
920 target_cell = this.insert_cell_below('raw',i);
922 target_cell = this.insert_cell_below('raw',i);
921 var text = source_cell.get_text();
923 var text = source_cell.get_text();
922 if (text === source_cell.placeholder) {
924 if (text === source_cell.placeholder) {
923 text = '';
925 text = '';
924 };
926 };
925 // The edit must come before the set_text.
927 // The edit must come before the set_text.
926 target_cell.edit();
928 target_cell.edit();
927 target_cell.set_text(text);
929 target_cell.set_text(text);
928 // make this value the starting point, so that we can only undo
930 // make this value the starting point, so that we can only undo
929 // to this state, instead of a blank cell
931 // to this state, instead of a blank cell
930 target_cell.code_mirror.clearHistory();
932 target_cell.code_mirror.clearHistory();
931 source_element.remove();
933 source_element.remove();
932 this.set_dirty(true);
934 this.set_dirty(true);
933 };
935 };
934 };
936 };
935 };
937 };
936
938
937 /**
939 /**
938 * Turn a cell into a heading cell.
940 * Turn a cell into a heading cell.
939 *
941 *
940 * @method to_heading
942 * @method to_heading
941 * @param {Number} [index] A cell's index
943 * @param {Number} [index] A cell's index
942 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
944 * @param {Number} [level] A heading level (e.g., 1 becomes &lt;h1&gt;)
943 */
945 */
944 Notebook.prototype.to_heading = function (index, level) {
946 Notebook.prototype.to_heading = function (index, level) {
945 level = level || 1;
947 level = level || 1;
946 var i = this.index_or_selected(index);
948 var i = this.index_or_selected(index);
947 if (this.is_valid_cell_index(i)) {
949 if (this.is_valid_cell_index(i)) {
948 var source_element = this.get_cell_element(i);
950 var source_element = this.get_cell_element(i);
949 var source_cell = source_element.data("cell");
951 var source_cell = source_element.data("cell");
950 var target_cell = null;
952 var target_cell = null;
951 if (source_cell instanceof IPython.HeadingCell) {
953 if (source_cell instanceof IPython.HeadingCell) {
952 source_cell.set_level(level);
954 source_cell.set_level(level);
953 } else {
955 } else {
954 target_cell = this.insert_cell_below('heading',i);
956 target_cell = this.insert_cell_below('heading',i);
955 var text = source_cell.get_text();
957 var text = source_cell.get_text();
956 if (text === source_cell.placeholder) {
958 if (text === source_cell.placeholder) {
957 text = '';
959 text = '';
958 };
960 };
959 // The edit must come before the set_text.
961 // The edit must come before the set_text.
960 target_cell.set_level(level);
962 target_cell.set_level(level);
961 target_cell.edit();
963 target_cell.edit();
962 target_cell.set_text(text);
964 target_cell.set_text(text);
963 // make this value the starting point, so that we can only undo
965 // make this value the starting point, so that we can only undo
964 // to this state, instead of a blank cell
966 // to this state, instead of a blank cell
965 target_cell.code_mirror.clearHistory();
967 target_cell.code_mirror.clearHistory();
966 source_element.remove();
968 source_element.remove();
967 this.set_dirty(true);
969 this.set_dirty(true);
968 };
970 };
969 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
971 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
970 {'cell_type':'heading',level:level}
972 {'cell_type':'heading',level:level}
971 );
973 );
972 };
974 };
973 };
975 };
974
976
975
977
976 // Cut/Copy/Paste
978 // Cut/Copy/Paste
977
979
978 /**
980 /**
979 * Enable UI elements for pasting cells.
981 * Enable UI elements for pasting cells.
980 *
982 *
981 * @method enable_paste
983 * @method enable_paste
982 */
984 */
983 Notebook.prototype.enable_paste = function () {
985 Notebook.prototype.enable_paste = function () {
984 var that = this;
986 var that = this;
985 if (!this.paste_enabled) {
987 if (!this.paste_enabled) {
986 $('#paste_cell_replace').removeClass('ui-state-disabled')
988 $('#paste_cell_replace').removeClass('ui-state-disabled')
987 .on('click', function () {that.paste_cell_replace();});
989 .on('click', function () {that.paste_cell_replace();});
988 $('#paste_cell_above').removeClass('ui-state-disabled')
990 $('#paste_cell_above').removeClass('ui-state-disabled')
989 .on('click', function () {that.paste_cell_above();});
991 .on('click', function () {that.paste_cell_above();});
990 $('#paste_cell_below').removeClass('ui-state-disabled')
992 $('#paste_cell_below').removeClass('ui-state-disabled')
991 .on('click', function () {that.paste_cell_below();});
993 .on('click', function () {that.paste_cell_below();});
992 this.paste_enabled = true;
994 this.paste_enabled = true;
993 };
995 };
994 };
996 };
995
997
996 /**
998 /**
997 * Disable UI elements for pasting cells.
999 * Disable UI elements for pasting cells.
998 *
1000 *
999 * @method disable_paste
1001 * @method disable_paste
1000 */
1002 */
1001 Notebook.prototype.disable_paste = function () {
1003 Notebook.prototype.disable_paste = function () {
1002 if (this.paste_enabled) {
1004 if (this.paste_enabled) {
1003 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
1005 $('#paste_cell_replace').addClass('ui-state-disabled').off('click');
1004 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
1006 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
1005 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
1007 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
1006 this.paste_enabled = false;
1008 this.paste_enabled = false;
1007 };
1009 };
1008 };
1010 };
1009
1011
1010 /**
1012 /**
1011 * Cut a cell.
1013 * Cut a cell.
1012 *
1014 *
1013 * @method cut_cell
1015 * @method cut_cell
1014 */
1016 */
1015 Notebook.prototype.cut_cell = function () {
1017 Notebook.prototype.cut_cell = function () {
1016 this.copy_cell();
1018 this.copy_cell();
1017 this.delete_cell();
1019 this.delete_cell();
1018 }
1020 }
1019
1021
1020 /**
1022 /**
1021 * Copy a cell.
1023 * Copy a cell.
1022 *
1024 *
1023 * @method copy_cell
1025 * @method copy_cell
1024 */
1026 */
1025 Notebook.prototype.copy_cell = function () {
1027 Notebook.prototype.copy_cell = function () {
1026 var cell = this.get_selected_cell();
1028 var cell = this.get_selected_cell();
1027 this.clipboard = cell.toJSON();
1029 this.clipboard = cell.toJSON();
1028 this.enable_paste();
1030 this.enable_paste();
1029 };
1031 };
1030
1032
1031 /**
1033 /**
1032 * Replace the selected cell with a cell in the clipboard.
1034 * Replace the selected cell with a cell in the clipboard.
1033 *
1035 *
1034 * @method paste_cell_replace
1036 * @method paste_cell_replace
1035 */
1037 */
1036 Notebook.prototype.paste_cell_replace = function () {
1038 Notebook.prototype.paste_cell_replace = function () {
1037 if (this.clipboard !== null && this.paste_enabled) {
1039 if (this.clipboard !== null && this.paste_enabled) {
1038 var cell_data = this.clipboard;
1040 var cell_data = this.clipboard;
1039 var new_cell = this.insert_cell_above(cell_data.cell_type);
1041 var new_cell = this.insert_cell_above(cell_data.cell_type);
1040 new_cell.fromJSON(cell_data);
1042 new_cell.fromJSON(cell_data);
1041 var old_cell = this.get_next_cell(new_cell);
1043 var old_cell = this.get_next_cell(new_cell);
1042 this.delete_cell(this.find_cell_index(old_cell));
1044 this.delete_cell(this.find_cell_index(old_cell));
1043 this.select(this.find_cell_index(new_cell));
1045 this.select(this.find_cell_index(new_cell));
1044 };
1046 };
1045 };
1047 };
1046
1048
1047 /**
1049 /**
1048 * Paste a cell from the clipboard above the selected cell.
1050 * Paste a cell from the clipboard above the selected cell.
1049 *
1051 *
1050 * @method paste_cell_above
1052 * @method paste_cell_above
1051 */
1053 */
1052 Notebook.prototype.paste_cell_above = function () {
1054 Notebook.prototype.paste_cell_above = function () {
1053 if (this.clipboard !== null && this.paste_enabled) {
1055 if (this.clipboard !== null && this.paste_enabled) {
1054 var cell_data = this.clipboard;
1056 var cell_data = this.clipboard;
1055 var new_cell = this.insert_cell_above(cell_data.cell_type);
1057 var new_cell = this.insert_cell_above(cell_data.cell_type);
1056 new_cell.fromJSON(cell_data);
1058 new_cell.fromJSON(cell_data);
1057 };
1059 };
1058 };
1060 };
1059
1061
1060 /**
1062 /**
1061 * Paste a cell from the clipboard below the selected cell.
1063 * Paste a cell from the clipboard below the selected cell.
1062 *
1064 *
1063 * @method paste_cell_below
1065 * @method paste_cell_below
1064 */
1066 */
1065 Notebook.prototype.paste_cell_below = function () {
1067 Notebook.prototype.paste_cell_below = function () {
1066 if (this.clipboard !== null && this.paste_enabled) {
1068 if (this.clipboard !== null && this.paste_enabled) {
1067 var cell_data = this.clipboard;
1069 var cell_data = this.clipboard;
1068 var new_cell = this.insert_cell_below(cell_data.cell_type);
1070 var new_cell = this.insert_cell_below(cell_data.cell_type);
1069 new_cell.fromJSON(cell_data);
1071 new_cell.fromJSON(cell_data);
1070 };
1072 };
1071 };
1073 };
1072
1074
1073 // Cell undelete
1075 // Cell undelete
1074
1076
1075 /**
1077 /**
1076 * Restore the most recently deleted cell.
1078 * Restore the most recently deleted cell.
1077 *
1079 *
1078 * @method undelete
1080 * @method undelete
1079 */
1081 */
1080 Notebook.prototype.undelete = function() {
1082 Notebook.prototype.undelete = function() {
1081 if (this.undelete_backup !== null && this.undelete_index !== null) {
1083 if (this.undelete_backup !== null && this.undelete_index !== null) {
1082 var current_index = this.get_selected_index();
1084 var current_index = this.get_selected_index();
1083 if (this.undelete_index < current_index) {
1085 if (this.undelete_index < current_index) {
1084 current_index = current_index + 1;
1086 current_index = current_index + 1;
1085 }
1087 }
1086 if (this.undelete_index >= this.ncells()) {
1088 if (this.undelete_index >= this.ncells()) {
1087 this.select(this.ncells() - 1);
1089 this.select(this.ncells() - 1);
1088 }
1090 }
1089 else {
1091 else {
1090 this.select(this.undelete_index);
1092 this.select(this.undelete_index);
1091 }
1093 }
1092 var cell_data = this.undelete_backup;
1094 var cell_data = this.undelete_backup;
1093 var new_cell = null;
1095 var new_cell = null;
1094 if (this.undelete_below) {
1096 if (this.undelete_below) {
1095 new_cell = this.insert_cell_below(cell_data.cell_type);
1097 new_cell = this.insert_cell_below(cell_data.cell_type);
1096 } else {
1098 } else {
1097 new_cell = this.insert_cell_above(cell_data.cell_type);
1099 new_cell = this.insert_cell_above(cell_data.cell_type);
1098 }
1100 }
1099 new_cell.fromJSON(cell_data);
1101 new_cell.fromJSON(cell_data);
1100 this.select(current_index);
1102 this.select(current_index);
1101 this.undelete_backup = null;
1103 this.undelete_backup = null;
1102 this.undelete_index = null;
1104 this.undelete_index = null;
1103 }
1105 }
1104 $('#undelete_cell').addClass('ui-state-disabled');
1106 $('#undelete_cell').addClass('ui-state-disabled');
1105 }
1107 }
1106
1108
1107 // Split/merge
1109 // Split/merge
1108
1110
1109 /**
1111 /**
1110 * Split the selected cell into two, at the cursor.
1112 * Split the selected cell into two, at the cursor.
1111 *
1113 *
1112 * @method split_cell
1114 * @method split_cell
1113 */
1115 */
1114 Notebook.prototype.split_cell = function () {
1116 Notebook.prototype.split_cell = function () {
1115 // Todo: implement spliting for other cell types.
1117 // Todo: implement spliting for other cell types.
1116 var cell = this.get_selected_cell();
1118 var cell = this.get_selected_cell();
1117 if (cell.is_splittable()) {
1119 if (cell.is_splittable()) {
1118 var texta = cell.get_pre_cursor();
1120 var texta = cell.get_pre_cursor();
1119 var textb = cell.get_post_cursor();
1121 var textb = cell.get_post_cursor();
1120 if (cell instanceof IPython.CodeCell) {
1122 if (cell instanceof IPython.CodeCell) {
1121 cell.set_text(texta);
1123 cell.set_text(texta);
1122 var new_cell = this.insert_cell_below('code');
1124 var new_cell = this.insert_cell_below('code');
1123 new_cell.set_text(textb);
1125 new_cell.set_text(textb);
1124 } else if (cell instanceof IPython.MarkdownCell) {
1126 } else if (cell instanceof IPython.MarkdownCell) {
1125 cell.set_text(texta);
1127 cell.set_text(texta);
1126 cell.render();
1128 cell.render();
1127 var new_cell = this.insert_cell_below('markdown');
1129 var new_cell = this.insert_cell_below('markdown');
1128 new_cell.edit(); // editor must be visible to call set_text
1130 new_cell.edit(); // editor must be visible to call set_text
1129 new_cell.set_text(textb);
1131 new_cell.set_text(textb);
1130 new_cell.render();
1132 new_cell.render();
1131 }
1133 }
1132 };
1134 };
1133 };
1135 };
1134
1136
1135 /**
1137 /**
1136 * Combine the selected cell into the cell above it.
1138 * Combine the selected cell into the cell above it.
1137 *
1139 *
1138 * @method merge_cell_above
1140 * @method merge_cell_above
1139 */
1141 */
1140 Notebook.prototype.merge_cell_above = function () {
1142 Notebook.prototype.merge_cell_above = function () {
1141 var index = this.get_selected_index();
1143 var index = this.get_selected_index();
1142 var cell = this.get_cell(index);
1144 var cell = this.get_cell(index);
1143 if (index > 0) {
1145 if (index > 0) {
1144 var upper_cell = this.get_cell(index-1);
1146 var upper_cell = this.get_cell(index-1);
1145 var upper_text = upper_cell.get_text();
1147 var upper_text = upper_cell.get_text();
1146 var text = cell.get_text();
1148 var text = cell.get_text();
1147 if (cell instanceof IPython.CodeCell) {
1149 if (cell instanceof IPython.CodeCell) {
1148 cell.set_text(upper_text+'\n'+text);
1150 cell.set_text(upper_text+'\n'+text);
1149 } else if (cell instanceof IPython.MarkdownCell) {
1151 } else if (cell instanceof IPython.MarkdownCell) {
1150 cell.edit();
1152 cell.edit();
1151 cell.set_text(upper_text+'\n'+text);
1153 cell.set_text(upper_text+'\n'+text);
1152 cell.render();
1154 cell.render();
1153 };
1155 };
1154 this.delete_cell(index-1);
1156 this.delete_cell(index-1);
1155 this.select(this.find_cell_index(cell));
1157 this.select(this.find_cell_index(cell));
1156 };
1158 };
1157 };
1159 };
1158
1160
1159 /**
1161 /**
1160 * Combine the selected cell into the cell below it.
1162 * Combine the selected cell into the cell below it.
1161 *
1163 *
1162 * @method merge_cell_below
1164 * @method merge_cell_below
1163 */
1165 */
1164 Notebook.prototype.merge_cell_below = function () {
1166 Notebook.prototype.merge_cell_below = function () {
1165 var index = this.get_selected_index();
1167 var index = this.get_selected_index();
1166 var cell = this.get_cell(index);
1168 var cell = this.get_cell(index);
1167 if (index < this.ncells()-1) {
1169 if (index < this.ncells()-1) {
1168 var lower_cell = this.get_cell(index+1);
1170 var lower_cell = this.get_cell(index+1);
1169 var lower_text = lower_cell.get_text();
1171 var lower_text = lower_cell.get_text();
1170 var text = cell.get_text();
1172 var text = cell.get_text();
1171 if (cell instanceof IPython.CodeCell) {
1173 if (cell instanceof IPython.CodeCell) {
1172 cell.set_text(text+'\n'+lower_text);
1174 cell.set_text(text+'\n'+lower_text);
1173 } else if (cell instanceof IPython.MarkdownCell) {
1175 } else if (cell instanceof IPython.MarkdownCell) {
1174 cell.edit();
1176 cell.edit();
1175 cell.set_text(text+'\n'+lower_text);
1177 cell.set_text(text+'\n'+lower_text);
1176 cell.render();
1178 cell.render();
1177 };
1179 };
1178 this.delete_cell(index+1);
1180 this.delete_cell(index+1);
1179 this.select(this.find_cell_index(cell));
1181 this.select(this.find_cell_index(cell));
1180 };
1182 };
1181 };
1183 };
1182
1184
1183
1185
1184 // Cell collapsing and output clearing
1186 // Cell collapsing and output clearing
1185
1187
1186 /**
1188 /**
1187 * Hide a cell's output.
1189 * Hide a cell's output.
1188 *
1190 *
1189 * @method collapse
1191 * @method collapse
1190 * @param {Number} index A cell's numeric index
1192 * @param {Number} index A cell's numeric index
1191 */
1193 */
1192 Notebook.prototype.collapse = function (index) {
1194 Notebook.prototype.collapse = function (index) {
1193 var i = this.index_or_selected(index);
1195 var i = this.index_or_selected(index);
1194 this.get_cell(i).collapse();
1196 this.get_cell(i).collapse();
1195 this.set_dirty(true);
1197 this.set_dirty(true);
1196 };
1198 };
1197
1199
1198 /**
1200 /**
1199 * Show a cell's output.
1201 * Show a cell's output.
1200 *
1202 *
1201 * @method expand
1203 * @method expand
1202 * @param {Number} index A cell's numeric index
1204 * @param {Number} index A cell's numeric index
1203 */
1205 */
1204 Notebook.prototype.expand = function (index) {
1206 Notebook.prototype.expand = function (index) {
1205 var i = this.index_or_selected(index);
1207 var i = this.index_or_selected(index);
1206 this.get_cell(i).expand();
1208 this.get_cell(i).expand();
1207 this.set_dirty(true);
1209 this.set_dirty(true);
1208 };
1210 };
1209
1211
1210 /** Toggle whether a cell's output is collapsed or expanded.
1212 /** Toggle whether a cell's output is collapsed or expanded.
1211 *
1213 *
1212 * @method toggle_output
1214 * @method toggle_output
1213 * @param {Number} index A cell's numeric index
1215 * @param {Number} index A cell's numeric index
1214 */
1216 */
1215 Notebook.prototype.toggle_output = function (index) {
1217 Notebook.prototype.toggle_output = function (index) {
1216 var i = this.index_or_selected(index);
1218 var i = this.index_or_selected(index);
1217 this.get_cell(i).toggle_output();
1219 this.get_cell(i).toggle_output();
1218 this.set_dirty(true);
1220 this.set_dirty(true);
1219 };
1221 };
1220
1222
1221 /**
1223 /**
1222 * Toggle a scrollbar for long cell outputs.
1224 * Toggle a scrollbar for long cell outputs.
1223 *
1225 *
1224 * @method toggle_output_scroll
1226 * @method toggle_output_scroll
1225 * @param {Number} index A cell's numeric index
1227 * @param {Number} index A cell's numeric index
1226 */
1228 */
1227 Notebook.prototype.toggle_output_scroll = function (index) {
1229 Notebook.prototype.toggle_output_scroll = function (index) {
1228 var i = this.index_or_selected(index);
1230 var i = this.index_or_selected(index);
1229 this.get_cell(i).toggle_output_scroll();
1231 this.get_cell(i).toggle_output_scroll();
1230 };
1232 };
1231
1233
1232 /**
1234 /**
1233 * Hide each code cell's output area.
1235 * Hide each code cell's output area.
1234 *
1236 *
1235 * @method collapse_all_output
1237 * @method collapse_all_output
1236 */
1238 */
1237 Notebook.prototype.collapse_all_output = function () {
1239 Notebook.prototype.collapse_all_output = function () {
1238 var ncells = this.ncells();
1240 var ncells = this.ncells();
1239 var cells = this.get_cells();
1241 var cells = this.get_cells();
1240 for (var i=0; i<ncells; i++) {
1242 for (var i=0; i<ncells; i++) {
1241 if (cells[i] instanceof IPython.CodeCell) {
1243 if (cells[i] instanceof IPython.CodeCell) {
1242 cells[i].output_area.collapse();
1244 cells[i].output_area.collapse();
1243 }
1245 }
1244 };
1246 };
1245 // this should not be set if the `collapse` key is removed from nbformat
1247 // this should not be set if the `collapse` key is removed from nbformat
1246 this.set_dirty(true);
1248 this.set_dirty(true);
1247 };
1249 };
1248
1250
1249 /**
1251 /**
1250 * Expand each code cell's output area, and add a scrollbar for long output.
1252 * Expand each code cell's output area, and add a scrollbar for long output.
1251 *
1253 *
1252 * @method scroll_all_output
1254 * @method scroll_all_output
1253 */
1255 */
1254 Notebook.prototype.scroll_all_output = function () {
1256 Notebook.prototype.scroll_all_output = function () {
1255 var ncells = this.ncells();
1257 var ncells = this.ncells();
1256 var cells = this.get_cells();
1258 var cells = this.get_cells();
1257 for (var i=0; i<ncells; i++) {
1259 for (var i=0; i<ncells; i++) {
1258 if (cells[i] instanceof IPython.CodeCell) {
1260 if (cells[i] instanceof IPython.CodeCell) {
1259 cells[i].output_area.expand();
1261 cells[i].output_area.expand();
1260 cells[i].output_area.scroll_if_long();
1262 cells[i].output_area.scroll_if_long();
1261 }
1263 }
1262 };
1264 };
1263 // this should not be set if the `collapse` key is removed from nbformat
1265 // this should not be set if the `collapse` key is removed from nbformat
1264 this.set_dirty(true);
1266 this.set_dirty(true);
1265 };
1267 };
1266
1268
1267 /**
1269 /**
1268 * Expand each code cell's output area, and remove scrollbars.
1270 * Expand each code cell's output area, and remove scrollbars.
1269 *
1271 *
1270 * @method expand_all_output
1272 * @method expand_all_output
1271 */
1273 */
1272 Notebook.prototype.expand_all_output = function () {
1274 Notebook.prototype.expand_all_output = function () {
1273 var ncells = this.ncells();
1275 var ncells = this.ncells();
1274 var cells = this.get_cells();
1276 var cells = this.get_cells();
1275 for (var i=0; i<ncells; i++) {
1277 for (var i=0; i<ncells; i++) {
1276 if (cells[i] instanceof IPython.CodeCell) {
1278 if (cells[i] instanceof IPython.CodeCell) {
1277 cells[i].output_area.expand();
1279 cells[i].output_area.expand();
1278 cells[i].output_area.unscroll_area();
1280 cells[i].output_area.unscroll_area();
1279 }
1281 }
1280 };
1282 };
1281 // this should not be set if the `collapse` key is removed from nbformat
1283 // this should not be set if the `collapse` key is removed from nbformat
1282 this.set_dirty(true);
1284 this.set_dirty(true);
1283 };
1285 };
1284
1286
1285 /**
1287 /**
1286 * Clear each code cell's output area.
1288 * Clear each code cell's output area.
1287 *
1289 *
1288 * @method clear_all_output
1290 * @method clear_all_output
1289 */
1291 */
1290 Notebook.prototype.clear_all_output = function () {
1292 Notebook.prototype.clear_all_output = function () {
1291 var ncells = this.ncells();
1293 var ncells = this.ncells();
1292 var cells = this.get_cells();
1294 var cells = this.get_cells();
1293 for (var i=0; i<ncells; i++) {
1295 for (var i=0; i<ncells; i++) {
1294 if (cells[i] instanceof IPython.CodeCell) {
1296 if (cells[i] instanceof IPython.CodeCell) {
1295 cells[i].clear_output(true,true,true);
1297 cells[i].clear_output(true,true,true);
1296 // Make all In[] prompts blank, as well
1298 // Make all In[] prompts blank, as well
1297 // TODO: make this configurable (via checkbox?)
1299 // TODO: make this configurable (via checkbox?)
1298 cells[i].set_input_prompt();
1300 cells[i].set_input_prompt();
1299 }
1301 }
1300 };
1302 };
1301 this.set_dirty(true);
1303 this.set_dirty(true);
1302 };
1304 };
1303
1305
1304
1306
1305 // Other cell functions: line numbers, ...
1307 // Other cell functions: line numbers, ...
1306
1308
1307 /**
1309 /**
1308 * Toggle line numbers in the selected cell's input area.
1310 * Toggle line numbers in the selected cell's input area.
1309 *
1311 *
1310 * @method cell_toggle_line_numbers
1312 * @method cell_toggle_line_numbers
1311 */
1313 */
1312 Notebook.prototype.cell_toggle_line_numbers = function() {
1314 Notebook.prototype.cell_toggle_line_numbers = function() {
1313 this.get_selected_cell().toggle_line_numbers();
1315 this.get_selected_cell().toggle_line_numbers();
1314 };
1316 };
1315
1317
1316 // Kernel related things
1318 // Kernel related things
1317
1319
1318 /**
1320 /**
1319 * Start a new kernel and set it on each code cell.
1321 * Start a new kernel and set it on each code cell.
1320 *
1322 *
1321 * @method start_kernel
1323 * @method start_kernel
1322 */
1324 */
1323 Notebook.prototype.start_kernel = function () {
1325 Notebook.prototype.start_kernel = function () {
1324 var base_url = $('body').data('baseKernelUrl') + "kernels";
1326 var base_url = $('body').data('baseKernelUrl') + "kernels";
1325 this.kernel = new IPython.Kernel(base_url);
1327 this.kernel = new IPython.Kernel(base_url);
1326 this.kernel.start(this.notebook_id);
1328 this.kernel.start(this.notebook_id);
1327 // Now that the kernel has been created, tell the CodeCells about it.
1329 // Now that the kernel has been created, tell the CodeCells about it.
1328 var ncells = this.ncells();
1330 var ncells = this.ncells();
1329 for (var i=0; i<ncells; i++) {
1331 for (var i=0; i<ncells; i++) {
1330 var cell = this.get_cell(i);
1332 var cell = this.get_cell(i);
1331 if (cell instanceof IPython.CodeCell) {
1333 if (cell instanceof IPython.CodeCell) {
1332 cell.set_kernel(this.kernel)
1334 cell.set_kernel(this.kernel)
1333 };
1335 };
1334 };
1336 };
1335 };
1337 };
1336
1338
1337 /**
1339 /**
1338 * Prompt the user to restart the IPython kernel.
1340 * Prompt the user to restart the IPython kernel.
1339 *
1341 *
1340 * @method restart_kernel
1342 * @method restart_kernel
1341 */
1343 */
1342 Notebook.prototype.restart_kernel = function () {
1344 Notebook.prototype.restart_kernel = function () {
1343 var that = this;
1345 var that = this;
1344 var dialog = $('<div/>');
1346 IPython.dialog.modal({
1345 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
1347 title : "Restart kernel or continue running?",
1346 $(document).append(dialog);
1348 body : $("<p/>").html(
1347 dialog.dialog({
1349 'Do you want to restart the current kernel? You will lose all variables defined in it.'
1348 resizable: false,
1350 ),
1349 modal: true,
1350 title: "Restart kernel or continue running?",
1351 closeText: '',
1352 buttons : {
1351 buttons : {
1353 "Restart": function () {
1352 "Continue running" : {},
1354 that.kernel.restart();
1353 "Restart" : {
1355 $(this).dialog('close');
1354 "class" : "btn-danger",
1356 },
1355 "click" : function() {
1357 "Continue running": function () {
1356 that.kernel.restart();
1358 $(this).dialog('close');
1357 }
1359 }
1358 }
1360 }
1359 }
1361 });
1360 });
1362 };
1361 };
1363
1362
1364 /**
1363 /**
1365 * Run the selected cell.
1364 * Run the selected cell.
1366 *
1365 *
1367 * Execute or render cell outputs.
1366 * Execute or render cell outputs.
1368 *
1367 *
1369 * @method execute_selected_cell
1368 * @method execute_selected_cell
1370 * @param {Object} options Customize post-execution behavior
1369 * @param {Object} options Customize post-execution behavior
1371 */
1370 */
1372 Notebook.prototype.execute_selected_cell = function (options) {
1371 Notebook.prototype.execute_selected_cell = function (options) {
1373 // add_new: should a new cell be added if we are at the end of the nb
1372 // add_new: should a new cell be added if we are at the end of the nb
1374 // terminal: execute in terminal mode, which stays in the current cell
1373 // terminal: execute in terminal mode, which stays in the current cell
1375 var default_options = {terminal: false, add_new: true};
1374 var default_options = {terminal: false, add_new: true};
1376 $.extend(default_options, options);
1375 $.extend(default_options, options);
1377 var that = this;
1376 var that = this;
1378 var cell = that.get_selected_cell();
1377 var cell = that.get_selected_cell();
1379 var cell_index = that.find_cell_index(cell);
1378 var cell_index = that.find_cell_index(cell);
1380 if (cell instanceof IPython.CodeCell) {
1379 if (cell instanceof IPython.CodeCell) {
1381 cell.execute();
1380 cell.execute();
1382 }
1381 }
1383 if (default_options.terminal) {
1382 if (default_options.terminal) {
1384 cell.select_all();
1383 cell.select_all();
1385 } else {
1384 } else {
1386 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1385 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1387 that.insert_cell_below('code');
1386 that.insert_cell_below('code');
1388 // If we are adding a new cell at the end, scroll down to show it.
1387 // If we are adding a new cell at the end, scroll down to show it.
1389 that.scroll_to_bottom();
1388 that.scroll_to_bottom();
1390 } else {
1389 } else {
1391 that.select(cell_index+1);
1390 that.select(cell_index+1);
1392 };
1391 };
1393 };
1392 };
1394 this.set_dirty(true);
1393 this.set_dirty(true);
1395 };
1394 };
1396
1395
1397 /**
1396 /**
1398 * Execute all cells below the selected cell.
1397 * Execute all cells below the selected cell.
1399 *
1398 *
1400 * @method execute_cells_below
1399 * @method execute_cells_below
1401 */
1400 */
1402 Notebook.prototype.execute_cells_below = function () {
1401 Notebook.prototype.execute_cells_below = function () {
1403 this.execute_cell_range(this.get_selected_index(), this.ncells());
1402 this.execute_cell_range(this.get_selected_index(), this.ncells());
1404 this.scroll_to_bottom();
1403 this.scroll_to_bottom();
1405 };
1404 };
1406
1405
1407 /**
1406 /**
1408 * Execute all cells above the selected cell.
1407 * Execute all cells above the selected cell.
1409 *
1408 *
1410 * @method execute_cells_above
1409 * @method execute_cells_above
1411 */
1410 */
1412 Notebook.prototype.execute_cells_above = function () {
1411 Notebook.prototype.execute_cells_above = function () {
1413 this.execute_cell_range(0, this.get_selected_index());
1412 this.execute_cell_range(0, this.get_selected_index());
1414 };
1413 };
1415
1414
1416 /**
1415 /**
1417 * Execute all cells.
1416 * Execute all cells.
1418 *
1417 *
1419 * @method execute_all_cells
1418 * @method execute_all_cells
1420 */
1419 */
1421 Notebook.prototype.execute_all_cells = function () {
1420 Notebook.prototype.execute_all_cells = function () {
1422 this.execute_cell_range(0, this.ncells());
1421 this.execute_cell_range(0, this.ncells());
1423 this.scroll_to_bottom();
1422 this.scroll_to_bottom();
1424 };
1423 };
1425
1424
1426 /**
1425 /**
1427 * Execute a contiguous range of cells.
1426 * Execute a contiguous range of cells.
1428 *
1427 *
1429 * @method execute_cell_range
1428 * @method execute_cell_range
1430 * @param {Number} start Index of the first cell to execute (inclusive)
1429 * @param {Number} start Index of the first cell to execute (inclusive)
1431 * @param {Number} end Index of the last cell to execute (exclusive)
1430 * @param {Number} end Index of the last cell to execute (exclusive)
1432 */
1431 */
1433 Notebook.prototype.execute_cell_range = function (start, end) {
1432 Notebook.prototype.execute_cell_range = function (start, end) {
1434 for (var i=start; i<end; i++) {
1433 for (var i=start; i<end; i++) {
1435 this.select(i);
1434 this.select(i);
1436 this.execute_selected_cell({add_new:false});
1435 this.execute_selected_cell({add_new:false});
1437 };
1436 };
1438 };
1437 };
1439
1438
1440 // Persistance and loading
1439 // Persistance and loading
1441
1440
1442 /**
1441 /**
1443 * Getter method for this notebook's ID.
1442 * Getter method for this notebook's ID.
1444 *
1443 *
1445 * @method get_notebook_id
1444 * @method get_notebook_id
1446 * @return {String} This notebook's ID
1445 * @return {String} This notebook's ID
1447 */
1446 */
1448 Notebook.prototype.get_notebook_id = function () {
1447 Notebook.prototype.get_notebook_id = function () {
1449 return this.notebook_id;
1448 return this.notebook_id;
1450 };
1449 };
1451
1450
1452 /**
1451 /**
1453 * Getter method for this notebook's name.
1452 * Getter method for this notebook's name.
1454 *
1453 *
1455 * @method get_notebook_name
1454 * @method get_notebook_name
1456 * @return {String} This notebook's name
1455 * @return {String} This notebook's name
1457 */
1456 */
1458 Notebook.prototype.get_notebook_name = function () {
1457 Notebook.prototype.get_notebook_name = function () {
1459 return this.notebook_name;
1458 return this.notebook_name;
1460 };
1459 };
1461
1460
1462 /**
1461 /**
1463 * Setter method for this notebook's name.
1462 * Setter method for this notebook's name.
1464 *
1463 *
1465 * @method set_notebook_name
1464 * @method set_notebook_name
1466 * @param {String} name A new name for this notebook
1465 * @param {String} name A new name for this notebook
1467 */
1466 */
1468 Notebook.prototype.set_notebook_name = function (name) {
1467 Notebook.prototype.set_notebook_name = function (name) {
1469 this.notebook_name = name;
1468 this.notebook_name = name;
1470 };
1469 };
1471
1470
1472 /**
1471 /**
1473 * Check that a notebook's name is valid.
1472 * Check that a notebook's name is valid.
1474 *
1473 *
1475 * @method test_notebook_name
1474 * @method test_notebook_name
1476 * @param {String} nbname A name for this notebook
1475 * @param {String} nbname A name for this notebook
1477 * @return {Boolean} True if the name is valid, false if invalid
1476 * @return {Boolean} True if the name is valid, false if invalid
1478 */
1477 */
1479 Notebook.prototype.test_notebook_name = function (nbname) {
1478 Notebook.prototype.test_notebook_name = function (nbname) {
1480 nbname = nbname || '';
1479 nbname = nbname || '';
1481 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1480 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1482 return true;
1481 return true;
1483 } else {
1482 } else {
1484 return false;
1483 return false;
1485 };
1484 };
1486 };
1485 };
1487
1486
1488 /**
1487 /**
1489 * Load a notebook from JSON (.ipynb).
1488 * Load a notebook from JSON (.ipynb).
1490 *
1489 *
1491 * This currently handles one worksheet: others are deleted.
1490 * This currently handles one worksheet: others are deleted.
1492 *
1491 *
1493 * @method fromJSON
1492 * @method fromJSON
1494 * @param {Object} data JSON representation of a notebook
1493 * @param {Object} data JSON representation of a notebook
1495 */
1494 */
1496 Notebook.prototype.fromJSON = function (data) {
1495 Notebook.prototype.fromJSON = function (data) {
1497 var ncells = this.ncells();
1496 var ncells = this.ncells();
1498 var i;
1497 var i;
1499 for (i=0; i<ncells; i++) {
1498 for (i=0; i<ncells; i++) {
1500 // Always delete cell 0 as they get renumbered as they are deleted.
1499 // Always delete cell 0 as they get renumbered as they are deleted.
1501 this.delete_cell(0);
1500 this.delete_cell(0);
1502 };
1501 };
1503 // Save the metadata and name.
1502 // Save the metadata and name.
1504 this.metadata = data.metadata;
1503 this.metadata = data.metadata;
1505 this.notebook_name = data.metadata.name;
1504 this.notebook_name = data.metadata.name;
1506 // Only handle 1 worksheet for now.
1505 // Only handle 1 worksheet for now.
1507 var worksheet = data.worksheets[0];
1506 var worksheet = data.worksheets[0];
1508 if (worksheet !== undefined) {
1507 if (worksheet !== undefined) {
1509 if (worksheet.metadata) {
1508 if (worksheet.metadata) {
1510 this.worksheet_metadata = worksheet.metadata;
1509 this.worksheet_metadata = worksheet.metadata;
1511 }
1510 }
1512 var new_cells = worksheet.cells;
1511 var new_cells = worksheet.cells;
1513 ncells = new_cells.length;
1512 ncells = new_cells.length;
1514 var cell_data = null;
1513 var cell_data = null;
1515 var new_cell = null;
1514 var new_cell = null;
1516 for (i=0; i<ncells; i++) {
1515 for (i=0; i<ncells; i++) {
1517 cell_data = new_cells[i];
1516 cell_data = new_cells[i];
1518 // VERSIONHACK: plaintext -> raw
1517 // VERSIONHACK: plaintext -> raw
1519 // handle never-released plaintext name for raw cells
1518 // handle never-released plaintext name for raw cells
1520 if (cell_data.cell_type === 'plaintext'){
1519 if (cell_data.cell_type === 'plaintext'){
1521 cell_data.cell_type = 'raw';
1520 cell_data.cell_type = 'raw';
1522 }
1521 }
1523
1522
1524 new_cell = this.insert_cell_below(cell_data.cell_type);
1523 new_cell = this.insert_cell_below(cell_data.cell_type);
1525 new_cell.fromJSON(cell_data);
1524 new_cell.fromJSON(cell_data);
1526 };
1525 };
1527 };
1526 };
1528 if (data.worksheets.length > 1) {
1527 if (data.worksheets.length > 1) {
1529 var dialog = $('<div/>');
1528 IPython.dialog.modal({
1530 dialog.html("This notebook has " + data.worksheets.length + " worksheets, " +
1529 title : "Multiple worksheets",
1531 "but this version of IPython can only handle the first. " +
1530 body : "This notebook has " + data.worksheets.length + " worksheets, " +
1532 "If you save this notebook, worksheets after the first will be lost."
1531 "but this version of IPython can only handle the first. " +
1533 );
1532 "If you save this notebook, worksheets after the first will be lost.",
1534 this.element.append(dialog);
1535 dialog.dialog({
1536 resizable: false,
1537 modal: true,
1538 title: "Multiple worksheets",
1539 closeText: "",
1540 close: function(event, ui) {$(this).dialog('destroy').remove();},
1541 buttons : {
1533 buttons : {
1542 "OK": function () {
1534 OK : {
1543 $(this).dialog('close');
1535 class : "btn-danger"
1544 }
1536 }
1545 },
1537 }
1546 width: 400
1547 });
1538 });
1548 }
1539 }
1549 };
1540 };
1550
1541
1551 /**
1542 /**
1552 * Dump this notebook into a JSON-friendly object.
1543 * Dump this notebook into a JSON-friendly object.
1553 *
1544 *
1554 * @method toJSON
1545 * @method toJSON
1555 * @return {Object} A JSON-friendly representation of this notebook.
1546 * @return {Object} A JSON-friendly representation of this notebook.
1556 */
1547 */
1557 Notebook.prototype.toJSON = function () {
1548 Notebook.prototype.toJSON = function () {
1558 var cells = this.get_cells();
1549 var cells = this.get_cells();
1559 var ncells = cells.length;
1550 var ncells = cells.length;
1560 var cell_array = new Array(ncells);
1551 var cell_array = new Array(ncells);
1561 for (var i=0; i<ncells; i++) {
1552 for (var i=0; i<ncells; i++) {
1562 cell_array[i] = cells[i].toJSON();
1553 cell_array[i] = cells[i].toJSON();
1563 };
1554 };
1564 var data = {
1555 var data = {
1565 // Only handle 1 worksheet for now.
1556 // Only handle 1 worksheet for now.
1566 worksheets : [{
1557 worksheets : [{
1567 cells: cell_array,
1558 cells: cell_array,
1568 metadata: this.worksheet_metadata
1559 metadata: this.worksheet_metadata
1569 }],
1560 }],
1570 metadata : this.metadata
1561 metadata : this.metadata
1571 };
1562 };
1572 return data;
1563 return data;
1573 };
1564 };
1574
1565
1575 /**
1566 /**
1576 * Start an autosave timer, for periodically saving the notebook.
1567 * Start an autosave timer, for periodically saving the notebook.
1577 *
1568 *
1578 * @method set_autosave_interval
1569 * @method set_autosave_interval
1579 * @param {Integer} interval the autosave interval in milliseconds
1570 * @param {Integer} interval the autosave interval in milliseconds
1580 */
1571 */
1581 Notebook.prototype.set_autosave_interval = function (interval) {
1572 Notebook.prototype.set_autosave_interval = function (interval) {
1582 var that = this;
1573 var that = this;
1583 // clear previous interval, so we don't get simultaneous timers
1574 // clear previous interval, so we don't get simultaneous timers
1584 if (this.autosave_timer) {
1575 if (this.autosave_timer) {
1585 clearInterval(this.autosave_timer);
1576 clearInterval(this.autosave_timer);
1586 }
1577 }
1587
1578
1588 this.autosave_interval = this.minimum_autosave_interval = interval;
1579 this.autosave_interval = this.minimum_autosave_interval = interval;
1589 if (interval) {
1580 if (interval) {
1590 this.autosave_timer = setInterval(function() {
1581 this.autosave_timer = setInterval(function() {
1591 if (that.dirty) {
1582 if (that.dirty) {
1592 that.save_notebook();
1583 that.save_notebook();
1593 }
1584 }
1594 }, interval);
1585 }, interval);
1595 $([IPython.events]).trigger("autosave_enabled.Notebook", interval);
1586 $([IPython.events]).trigger("autosave_enabled.Notebook", interval);
1596 } else {
1587 } else {
1597 this.autosave_timer = null;
1588 this.autosave_timer = null;
1598 $([IPython.events]).trigger("autosave_disabled.Notebook");
1589 $([IPython.events]).trigger("autosave_disabled.Notebook");
1599 };
1590 };
1600 };
1591 };
1601
1592
1602 /**
1593 /**
1603 * Save this notebook on the server.
1594 * Save this notebook on the server.
1604 *
1595 *
1605 * @method save_notebook
1596 * @method save_notebook
1606 */
1597 */
1607 Notebook.prototype.save_notebook = function () {
1598 Notebook.prototype.save_notebook = function () {
1608 // We may want to move the name/id/nbformat logic inside toJSON?
1599 // We may want to move the name/id/nbformat logic inside toJSON?
1609 var data = this.toJSON();
1600 var data = this.toJSON();
1610 data.metadata.name = this.notebook_name;
1601 data.metadata.name = this.notebook_name;
1611 data.nbformat = this.nbformat;
1602 data.nbformat = this.nbformat;
1612 data.nbformat_minor = this.nbformat_minor;
1603 data.nbformat_minor = this.nbformat_minor;
1613
1604
1614 // time the ajax call for autosave tuning purposes.
1605 // time the ajax call for autosave tuning purposes.
1615 var start = new Date().getTime();
1606 var start = new Date().getTime();
1616
1607
1617 // We do the call with settings so we can set cache to false.
1608 // We do the call with settings so we can set cache to false.
1618 var settings = {
1609 var settings = {
1619 processData : false,
1610 processData : false,
1620 cache : false,
1611 cache : false,
1621 type : "PUT",
1612 type : "PUT",
1622 data : JSON.stringify(data),
1613 data : JSON.stringify(data),
1623 headers : {'Content-Type': 'application/json'},
1614 headers : {'Content-Type': 'application/json'},
1624 success : $.proxy(this.save_notebook_success, this, start),
1615 success : $.proxy(this.save_notebook_success, this, start),
1625 error : $.proxy(this.save_notebook_error, this)
1616 error : $.proxy(this.save_notebook_error, this)
1626 };
1617 };
1627 $([IPython.events]).trigger('notebook_saving.Notebook');
1618 $([IPython.events]).trigger('notebook_saving.Notebook');
1628 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1619 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1629 $.ajax(url, settings);
1620 $.ajax(url, settings);
1630 };
1621 };
1631
1622
1632 /**
1623 /**
1633 * Success callback for saving a notebook.
1624 * Success callback for saving a notebook.
1634 *
1625 *
1635 * @method save_notebook_success
1626 * @method save_notebook_success
1636 * @param {Integer} start the time when the save request started
1627 * @param {Integer} start the time when the save request started
1637 * @param {Object} data JSON representation of a notebook
1628 * @param {Object} data JSON representation of a notebook
1638 * @param {String} status Description of response status
1629 * @param {String} status Description of response status
1639 * @param {jqXHR} xhr jQuery Ajax object
1630 * @param {jqXHR} xhr jQuery Ajax object
1640 */
1631 */
1641 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1632 Notebook.prototype.save_notebook_success = function (start, data, status, xhr) {
1642 this.set_dirty(false);
1633 this.set_dirty(false);
1643 $([IPython.events]).trigger('notebook_saved.Notebook');
1634 $([IPython.events]).trigger('notebook_saved.Notebook');
1644 this._update_autosave_interval(start);
1635 this._update_autosave_interval(start);
1645 if (this._checkpoint_after_save) {
1636 if (this._checkpoint_after_save) {
1646 this.create_checkpoint();
1637 this.create_checkpoint();
1647 this._checkpoint_after_save = false;
1638 this._checkpoint_after_save = false;
1648 };
1639 };
1649 };
1640 };
1650
1641
1651 /**
1642 /**
1652 * update the autosave interval based on how long the last save took
1643 * update the autosave interval based on how long the last save took
1653 *
1644 *
1654 * @method _update_autosave_interval
1645 * @method _update_autosave_interval
1655 * @param {Integer} timestamp when the save request started
1646 * @param {Integer} timestamp when the save request started
1656 */
1647 */
1657 Notebook.prototype._update_autosave_interval = function (start) {
1648 Notebook.prototype._update_autosave_interval = function (start) {
1658 var duration = (new Date().getTime() - start);
1649 var duration = (new Date().getTime() - start);
1659 if (this.autosave_interval) {
1650 if (this.autosave_interval) {
1660 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1651 // new save interval: higher of 10x save duration or parameter (default 30 seconds)
1661 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1652 var interval = Math.max(10 * duration, this.minimum_autosave_interval);
1662 // round to 10 seconds, otherwise we will be setting a new interval too often
1653 // round to 10 seconds, otherwise we will be setting a new interval too often
1663 interval = 10000 * Math.round(interval / 10000);
1654 interval = 10000 * Math.round(interval / 10000);
1664 // set new interval, if it's changed
1655 // set new interval, if it's changed
1665 if (interval != this.autosave_interval) {
1656 if (interval != this.autosave_interval) {
1666 this.set_autosave_interval(interval);
1657 this.set_autosave_interval(interval);
1667 }
1658 }
1668 }
1659 }
1669 };
1660 };
1670
1661
1671 /**
1662 /**
1672 * Failure callback for saving a notebook.
1663 * Failure callback for saving a notebook.
1673 *
1664 *
1674 * @method save_notebook_error
1665 * @method save_notebook_error
1675 * @param {jqXHR} xhr jQuery Ajax object
1666 * @param {jqXHR} xhr jQuery Ajax object
1676 * @param {String} status Description of response status
1667 * @param {String} status Description of response status
1677 * @param {String} error_msg HTTP error message
1668 * @param {String} error_msg HTTP error message
1678 */
1669 */
1679 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1670 Notebook.prototype.save_notebook_error = function (xhr, status, error_msg) {
1680 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1671 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1681 };
1672 };
1682
1673
1683 /**
1674 /**
1684 * Request a notebook's data from the server.
1675 * Request a notebook's data from the server.
1685 *
1676 *
1686 * @method load_notebook
1677 * @method load_notebook
1687 * @param {String} notebook_id A notebook to load
1678 * @param {String} notebook_id A notebook to load
1688 */
1679 */
1689 Notebook.prototype.load_notebook = function (notebook_id) {
1680 Notebook.prototype.load_notebook = function (notebook_id) {
1690 var that = this;
1681 var that = this;
1691 this.notebook_id = notebook_id;
1682 this.notebook_id = notebook_id;
1692 // We do the call with settings so we can set cache to false.
1683 // We do the call with settings so we can set cache to false.
1693 var settings = {
1684 var settings = {
1694 processData : false,
1685 processData : false,
1695 cache : false,
1686 cache : false,
1696 type : "GET",
1687 type : "GET",
1697 dataType : "json",
1688 dataType : "json",
1698 success : $.proxy(this.load_notebook_success,this),
1689 success : $.proxy(this.load_notebook_success,this),
1699 error : $.proxy(this.load_notebook_error,this),
1690 error : $.proxy(this.load_notebook_error,this),
1700 };
1691 };
1701 $([IPython.events]).trigger('notebook_loading.Notebook');
1692 $([IPython.events]).trigger('notebook_loading.Notebook');
1702 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1693 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id;
1703 $.ajax(url, settings);
1694 $.ajax(url, settings);
1704 };
1695 };
1705
1696
1706 /**
1697 /**
1707 * Success callback for loading a notebook from the server.
1698 * Success callback for loading a notebook from the server.
1708 *
1699 *
1709 * Load notebook data from the JSON response.
1700 * Load notebook data from the JSON response.
1710 *
1701 *
1711 * @method load_notebook_success
1702 * @method load_notebook_success
1712 * @param {Object} data JSON representation of a notebook
1703 * @param {Object} data JSON representation of a notebook
1713 * @param {String} status Description of response status
1704 * @param {String} status Description of response status
1714 * @param {jqXHR} xhr jQuery Ajax object
1705 * @param {jqXHR} xhr jQuery Ajax object
1715 */
1706 */
1716 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1707 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1717 this.fromJSON(data);
1708 this.fromJSON(data);
1718 if (this.ncells() === 0) {
1709 if (this.ncells() === 0) {
1719 this.insert_cell_below('code');
1710 this.insert_cell_below('code');
1720 };
1711 };
1721 this.set_dirty(false);
1712 this.set_dirty(false);
1722 this.select(0);
1713 this.select(0);
1723 this.scroll_to_top();
1714 this.scroll_to_top();
1724 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1715 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1725 msg = "This notebook has been converted from an older " +
1716 var msg = "This notebook has been converted from an older " +
1726 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1717 "notebook format (v"+data.orig_nbformat+") to the current notebook " +
1727 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1718 "format (v"+data.nbformat+"). The next time you save this notebook, the " +
1728 "newer notebook format will be used and older verions of IPython " +
1719 "newer notebook format will be used and older versions of IPython " +
1729 "may not be able to read it. To keep the older version, close the " +
1720 "may not be able to read it. To keep the older version, close the " +
1730 "notebook without saving it.";
1721 "notebook without saving it.";
1731 var dialog = $('<div/>');
1722 IPython.dialog.modal({
1732 dialog.html(msg);
1723 title : "Notebook converted",
1733 this.element.append(dialog);
1724 body : msg,
1734 dialog.dialog({
1735 resizable: false,
1736 modal: true,
1737 title: "Notebook converted",
1738 closeText: "",
1739 close: function(event, ui) {$(this).dialog('destroy').remove();},
1740 buttons : {
1725 buttons : {
1741 "OK": function () {
1726 OK : {
1742 $(this).dialog('close');
1727 class : "btn-primary"
1743 }
1728 }
1744 },
1729 }
1745 width: 400
1746 });
1730 });
1747 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1731 } else if (data.orig_nbformat_minor !== undefined && data.nbformat_minor !== data.orig_nbformat_minor) {
1748 var that = this;
1732 var that = this;
1749 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1733 var orig_vs = 'v' + data.nbformat + '.' + data.orig_nbformat_minor;
1750 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1734 var this_vs = 'v' + data.nbformat + '.' + this.nbformat_minor;
1751 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1735 var msg = "This notebook is version " + orig_vs + ", but we only fully support up to " +
1752 this_vs + ". You can still work with this notebook, but some features " +
1736 this_vs + ". You can still work with this notebook, but some features " +
1753 "introduced in later notebook versions may not be available."
1737 "introduced in later notebook versions may not be available."
1754
1738
1755 var dialog = $('<div/>');
1739 IPython.dialog.modal({
1756 dialog.html(msg);
1740 title : "Newer Notebook",
1757 this.element.append(dialog);
1741 body : msg,
1758 dialog.dialog({
1759 resizable: false,
1760 modal: true,
1761 title: "Newer Notebook",
1762 closeText: "",
1763 close: function(event, ui) {$(this).dialog('destroy').remove();},
1764 buttons : {
1742 buttons : {
1765 "OK": function () {
1743 OK : {
1766 $(this).dialog('close');
1744 class : "btn-danger"
1767 }
1745 }
1768 },
1746 }
1769 width: 400
1770 });
1747 });
1771
1748
1772 }
1749 }
1773
1750
1774 // Create the kernel after the notebook is completely loaded to prevent
1751 // Create the kernel after the notebook is completely loaded to prevent
1775 // code execution upon loading, which is a security risk.
1752 // code execution upon loading, which is a security risk.
1776 if (! this.read_only) {
1753 if (! this.read_only) {
1777 this.start_kernel();
1754 this.start_kernel();
1778 // load our checkpoint list
1755 // load our checkpoint list
1779 IPython.notebook.list_checkpoints();
1756 IPython.notebook.list_checkpoints();
1780 }
1757 }
1781 $([IPython.events]).trigger('notebook_loaded.Notebook');
1758 $([IPython.events]).trigger('notebook_loaded.Notebook');
1782 };
1759 };
1783
1760
1784 /**
1761 /**
1785 * Failure callback for loading a notebook from the server.
1762 * Failure callback for loading a notebook from the server.
1786 *
1763 *
1787 * @method load_notebook_error
1764 * @method load_notebook_error
1788 * @param {jqXHR} xhr jQuery Ajax object
1765 * @param {jqXHR} xhr jQuery Ajax object
1789 * @param {String} textStatus Description of response status
1766 * @param {String} textStatus Description of response status
1790 * @param {String} errorThrow HTTP error message
1767 * @param {String} errorThrow HTTP error message
1791 */
1768 */
1792 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1769 Notebook.prototype.load_notebook_error = function (xhr, textStatus, errorThrow) {
1793 if (xhr.status === 500) {
1770 if (xhr.status === 500) {
1794 var msg = "An error occurred while loading this notebook. Most likely " +
1771 var msg = "An error occurred while loading this notebook. Most likely " +
1795 "this notebook is in a newer format than is supported by this " +
1772 "this notebook is in a newer format than is supported by this " +
1796 "version of IPython. This version can load notebook formats " +
1773 "version of IPython. This version can load notebook formats " +
1797 "v"+this.nbformat+" or earlier.";
1774 "v"+this.nbformat+" or earlier.";
1798 var dialog = $('<div/>');
1775
1799 dialog.html(msg);
1776 IPython.dialog.modal({
1800 this.element.append(dialog);
1801 dialog.dialog({
1802 resizable: false,
1803 modal: true,
1804 title: "Error loading notebook",
1777 title: "Error loading notebook",
1805 closeText: "",
1778 body : msg,
1806 close: function(event, ui) {$(this).dialog('destroy').remove();},
1807 buttons : {
1779 buttons : {
1808 "OK": function () {
1780 "OK": {}
1809 $(this).dialog('close');
1781 }
1810 }
1811 },
1812 width: 400
1813 });
1782 });
1814 }
1783 }
1815 }
1784 }
1816
1785
1817 /********************* checkpoint-related *********************/
1786 /********************* checkpoint-related *********************/
1818
1787
1819 /**
1788 /**
1820 * Save the notebook then immediately create a checkpoint.
1789 * Save the notebook then immediately create a checkpoint.
1821 *
1790 *
1822 * @method save_checkpoint
1791 * @method save_checkpoint
1823 */
1792 */
1824 Notebook.prototype.save_checkpoint = function () {
1793 Notebook.prototype.save_checkpoint = function () {
1825 this._checkpoint_after_save = true;
1794 this._checkpoint_after_save = true;
1826 this.save_notebook();
1795 this.save_notebook();
1827 };
1796 };
1828
1797
1829 /**
1798 /**
1830 * List checkpoints for this notebook.
1799 * List checkpoints for this notebook.
1831 *
1800 *
1832 * @method list_checkpoint
1801 * @method list_checkpoint
1833 */
1802 */
1834 Notebook.prototype.list_checkpoints = function () {
1803 Notebook.prototype.list_checkpoints = function () {
1835 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints';
1804 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints';
1836 $.get(url).done(
1805 $.get(url).done(
1837 $.proxy(this.list_checkpoints_success, this)
1806 $.proxy(this.list_checkpoints_success, this)
1838 ).fail(
1807 ).fail(
1839 $.proxy(this.list_checkpoints_error, this)
1808 $.proxy(this.list_checkpoints_error, this)
1840 );
1809 );
1841 };
1810 };
1842
1811
1843 /**
1812 /**
1844 * Success callback for listing checkpoints.
1813 * Success callback for listing checkpoints.
1845 *
1814 *
1846 * @method list_checkpoint_success
1815 * @method list_checkpoint_success
1847 * @param {Object} data JSON representation of a checkpoint
1816 * @param {Object} data JSON representation of a checkpoint
1848 * @param {String} status Description of response status
1817 * @param {String} status Description of response status
1849 * @param {jqXHR} xhr jQuery Ajax object
1818 * @param {jqXHR} xhr jQuery Ajax object
1850 */
1819 */
1851 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
1820 Notebook.prototype.list_checkpoints_success = function (data, status, xhr) {
1852 var data = $.parseJSON(data);
1821 var data = $.parseJSON(data);
1853 if (data.length) {
1822 if (data.length) {
1854 this.last_checkpoint = data[0];
1823 this.last_checkpoint = data[0];
1855 } else {
1824 } else {
1856 this.last_checkpoint = null;
1825 this.last_checkpoint = null;
1857 }
1826 }
1858 $([IPython.events]).trigger('checkpoints_listed.Notebook', [data]);
1827 $([IPython.events]).trigger('checkpoints_listed.Notebook', [data]);
1859 };
1828 };
1860
1829
1861 /**
1830 /**
1862 * Failure callback for listing a checkpoint.
1831 * Failure callback for listing a checkpoint.
1863 *
1832 *
1864 * @method list_checkpoint_error
1833 * @method list_checkpoint_error
1865 * @param {jqXHR} xhr jQuery Ajax object
1834 * @param {jqXHR} xhr jQuery Ajax object
1866 * @param {String} status Description of response status
1835 * @param {String} status Description of response status
1867 * @param {String} error_msg HTTP error message
1836 * @param {String} error_msg HTTP error message
1868 */
1837 */
1869 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
1838 Notebook.prototype.list_checkpoints_error = function (xhr, status, error_msg) {
1870 $([IPython.events]).trigger('list_checkpoints_failed.Notebook');
1839 $([IPython.events]).trigger('list_checkpoints_failed.Notebook');
1871 };
1840 };
1872
1841
1873 /**
1842 /**
1874 * Create a checkpoint of this notebook on the server from the most recent save.
1843 * Create a checkpoint of this notebook on the server from the most recent save.
1875 *
1844 *
1876 * @method create_checkpoint
1845 * @method create_checkpoint
1877 */
1846 */
1878 Notebook.prototype.create_checkpoint = function () {
1847 Notebook.prototype.create_checkpoint = function () {
1879 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints';
1848 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints';
1880 $.post(url).done(
1849 $.post(url).done(
1881 $.proxy(this.create_checkpoint_success, this)
1850 $.proxy(this.create_checkpoint_success, this)
1882 ).fail(
1851 ).fail(
1883 $.proxy(this.create_checkpoint_error, this)
1852 $.proxy(this.create_checkpoint_error, this)
1884 );
1853 );
1885 };
1854 };
1886
1855
1887 /**
1856 /**
1888 * Success callback for creating a checkpoint.
1857 * Success callback for creating a checkpoint.
1889 *
1858 *
1890 * @method create_checkpoint_success
1859 * @method create_checkpoint_success
1891 * @param {Object} data JSON representation of a checkpoint
1860 * @param {Object} data JSON representation of a checkpoint
1892 * @param {String} status Description of response status
1861 * @param {String} status Description of response status
1893 * @param {jqXHR} xhr jQuery Ajax object
1862 * @param {jqXHR} xhr jQuery Ajax object
1894 */
1863 */
1895 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
1864 Notebook.prototype.create_checkpoint_success = function (data, status, xhr) {
1896 var data = $.parseJSON(data);
1865 var data = $.parseJSON(data);
1897 this.last_checkpoint = data;
1866 this.last_checkpoint = data;
1898 $([IPython.events]).trigger('checkpoint_created.Notebook', data);
1867 $([IPython.events]).trigger('checkpoint_created.Notebook', data);
1899 };
1868 };
1900
1869
1901 /**
1870 /**
1902 * Failure callback for creating a checkpoint.
1871 * Failure callback for creating a checkpoint.
1903 *
1872 *
1904 * @method create_checkpoint_error
1873 * @method create_checkpoint_error
1905 * @param {jqXHR} xhr jQuery Ajax object
1874 * @param {jqXHR} xhr jQuery Ajax object
1906 * @param {String} status Description of response status
1875 * @param {String} status Description of response status
1907 * @param {String} error_msg HTTP error message
1876 * @param {String} error_msg HTTP error message
1908 */
1877 */
1909 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
1878 Notebook.prototype.create_checkpoint_error = function (xhr, status, error_msg) {
1910 $([IPython.events]).trigger('checkpoint_failed.Notebook');
1879 $([IPython.events]).trigger('checkpoint_failed.Notebook');
1911 };
1880 };
1912
1881
1913 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
1882 Notebook.prototype.restore_checkpoint_dialog = function (checkpoint) {
1914 var that = this;
1883 var that = this;
1915 var checkpoint = checkpoint || this.last_checkpoint;
1884 var checkpoint = checkpoint || this.last_checkpoint;
1916 if ( ! checkpoint ) {
1885 if ( ! checkpoint ) {
1917 console.log("restore dialog, but no checkpoint to restore to!");
1886 console.log("restore dialog, but no checkpoint to restore to!");
1918 return;
1887 return;
1919 }
1888 }
1920 var dialog = $('<div/>').append(
1889 var body = $('<div/>').append(
1921 $('<p/>').addClass("p-space").text(
1890 $('<p/>').addClass("p-space").text(
1922 "Are you sure you want to revert the notebook to " +
1891 "Are you sure you want to revert the notebook to " +
1923 "the latest checkpoint?"
1892 "the latest checkpoint?"
1924 ).append(
1893 ).append(
1925 $("<strong/>").text(
1894 $("<strong/>").text(
1926 " This cannot be undone."
1895 " This cannot be undone."
1927 )
1896 )
1928 )
1897 )
1929 ).append(
1898 ).append(
1930 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
1899 $('<p/>').addClass("p-space").text("The checkpoint was last updated at:")
1931 ).append(
1900 ).append(
1932 $('<p/>').addClass("p-space").text(
1901 $('<p/>').addClass("p-space").text(
1933 Date(checkpoint.last_modified)
1902 Date(checkpoint.last_modified)
1934 ).css("text-align", "center")
1903 ).css("text-align", "center")
1935 );
1904 );
1936
1905
1937 $(document).append(dialog);
1906 IPython.dialog.modal({
1938
1907 title : "Revert notebook to checkpoint",
1939 dialog.dialog({
1908 body : body,
1940 resizable: false,
1941 modal: true,
1942 title: "Revert notebook to checkpoint",
1943 closeText: '',
1944 buttons : {
1909 buttons : {
1945 "Revert": function () {
1910 Revert : {
1946 that.restore_checkpoint(checkpoint.checkpoint_id);
1911 class : "btn-danger",
1947 $(this).dialog('close');
1912 click : function () {
1913 that.restore_checkpoint(checkpoint.checkpoint_id);
1914 }
1948 },
1915 },
1949 "Cancel": function () {
1916 Cancel : {}
1950 $(this).dialog('close');
1951 }
1917 }
1952 },
1953 width: 400
1954 });
1918 });
1955 }
1919 }
1956
1920
1957 /**
1921 /**
1958 * Restore the notebook to a checkpoint state.
1922 * Restore the notebook to a checkpoint state.
1959 *
1923 *
1960 * @method restore_checkpoint
1924 * @method restore_checkpoint
1961 * @param {String} checkpoint ID
1925 * @param {String} checkpoint ID
1962 */
1926 */
1963 Notebook.prototype.restore_checkpoint = function (checkpoint) {
1927 Notebook.prototype.restore_checkpoint = function (checkpoint) {
1964 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
1928 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
1965 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint;
1929 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint;
1966 $.post(url).done(
1930 $.post(url).done(
1967 $.proxy(this.restore_checkpoint_success, this)
1931 $.proxy(this.restore_checkpoint_success, this)
1968 ).fail(
1932 ).fail(
1969 $.proxy(this.restore_checkpoint_error, this)
1933 $.proxy(this.restore_checkpoint_error, this)
1970 );
1934 );
1971 };
1935 };
1972
1936
1973 /**
1937 /**
1974 * Success callback for restoring a notebook to a checkpoint.
1938 * Success callback for restoring a notebook to a checkpoint.
1975 *
1939 *
1976 * @method restore_checkpoint_success
1940 * @method restore_checkpoint_success
1977 * @param {Object} data (ignored, should be empty)
1941 * @param {Object} data (ignored, should be empty)
1978 * @param {String} status Description of response status
1942 * @param {String} status Description of response status
1979 * @param {jqXHR} xhr jQuery Ajax object
1943 * @param {jqXHR} xhr jQuery Ajax object
1980 */
1944 */
1981 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
1945 Notebook.prototype.restore_checkpoint_success = function (data, status, xhr) {
1982 $([IPython.events]).trigger('checkpoint_restored.Notebook');
1946 $([IPython.events]).trigger('checkpoint_restored.Notebook');
1983 this.load_notebook(this.notebook_id);
1947 this.load_notebook(this.notebook_id);
1984 };
1948 };
1985
1949
1986 /**
1950 /**
1987 * Failure callback for restoring a notebook to a checkpoint.
1951 * Failure callback for restoring a notebook to a checkpoint.
1988 *
1952 *
1989 * @method restore_checkpoint_error
1953 * @method restore_checkpoint_error
1990 * @param {jqXHR} xhr jQuery Ajax object
1954 * @param {jqXHR} xhr jQuery Ajax object
1991 * @param {String} status Description of response status
1955 * @param {String} status Description of response status
1992 * @param {String} error_msg HTTP error message
1956 * @param {String} error_msg HTTP error message
1993 */
1957 */
1994 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
1958 Notebook.prototype.restore_checkpoint_error = function (xhr, status, error_msg) {
1995 $([IPython.events]).trigger('checkpoint_restore_failed.Notebook');
1959 $([IPython.events]).trigger('checkpoint_restore_failed.Notebook');
1996 };
1960 };
1997
1961
1998 /**
1962 /**
1999 * Delete a notebook checkpoint.
1963 * Delete a notebook checkpoint.
2000 *
1964 *
2001 * @method delete_checkpoint
1965 * @method delete_checkpoint
2002 * @param {String} checkpoint ID
1966 * @param {String} checkpoint ID
2003 */
1967 */
2004 Notebook.prototype.delete_checkpoint = function (checkpoint) {
1968 Notebook.prototype.delete_checkpoint = function (checkpoint) {
2005 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
1969 $([IPython.events]).trigger('notebook_restoring.Notebook', checkpoint);
2006 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint;
1970 var url = this.baseProjectUrl() + 'notebooks/' + this.notebook_id + '/checkpoints/' + checkpoint;
2007 $.ajax(url, {
1971 $.ajax(url, {
2008 type: 'DELETE',
1972 type: 'DELETE',
2009 success: $.proxy(this.delete_checkpoint_success, this),
1973 success: $.proxy(this.delete_checkpoint_success, this),
2010 error: $.proxy(this.delete_notebook_error,this)
1974 error: $.proxy(this.delete_notebook_error,this)
2011 });
1975 });
2012 };
1976 };
2013
1977
2014 /**
1978 /**
2015 * Success callback for deleting a notebook checkpoint
1979 * Success callback for deleting a notebook checkpoint
2016 *
1980 *
2017 * @method delete_checkpoint_success
1981 * @method delete_checkpoint_success
2018 * @param {Object} data (ignored, should be empty)
1982 * @param {Object} data (ignored, should be empty)
2019 * @param {String} status Description of response status
1983 * @param {String} status Description of response status
2020 * @param {jqXHR} xhr jQuery Ajax object
1984 * @param {jqXHR} xhr jQuery Ajax object
2021 */
1985 */
2022 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
1986 Notebook.prototype.delete_checkpoint_success = function (data, status, xhr) {
2023 $([IPython.events]).trigger('checkpoint_deleted.Notebook', data);
1987 $([IPython.events]).trigger('checkpoint_deleted.Notebook', data);
2024 this.load_notebook(this.notebook_id);
1988 this.load_notebook(this.notebook_id);
2025 };
1989 };
2026
1990
2027 /**
1991 /**
2028 * Failure callback for deleting a notebook checkpoint.
1992 * Failure callback for deleting a notebook checkpoint.
2029 *
1993 *
2030 * @method delete_checkpoint_error
1994 * @method delete_checkpoint_error
2031 * @param {jqXHR} xhr jQuery Ajax object
1995 * @param {jqXHR} xhr jQuery Ajax object
2032 * @param {String} status Description of response status
1996 * @param {String} status Description of response status
2033 * @param {String} error_msg HTTP error message
1997 * @param {String} error_msg HTTP error message
2034 */
1998 */
2035 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
1999 Notebook.prototype.delete_checkpoint_error = function (xhr, status, error_msg) {
2036 $([IPython.events]).trigger('checkpoint_delete_failed.Notebook');
2000 $([IPython.events]).trigger('checkpoint_delete_failed.Notebook');
2037 };
2001 };
2038
2002
2039
2003
2040 IPython.Notebook = Notebook;
2004 IPython.Notebook = Notebook;
2041
2005
2042
2006
2043 return IPython;
2007 return IPython;
2044
2008
2045 }(IPython));
2009 }(IPython));
2046
2010
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed, binary diff hidden
NO CONTENT: file was removed, binary diff hidden
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now