##// END OF EJS Templates
Improving the scrolling model.
Brian E. Granger -
Show More
@@ -1,251 +1,248 b''
1 /**
1 /**
2 * HTML5 ✰ Boilerplate
2 * HTML5 ✰ Boilerplate
3 *
3 *
4 * style.css contains a reset, font normalization and some base styles.
4 * style.css contains a reset, font normalization and some base styles.
5 *
5 *
6 * Credit is left where credit is due.
6 * Credit is left where credit is due.
7 * Much inspiration was taken from these projects:
7 * Much inspiration was taken from these projects:
8 * - yui.yahooapis.com/2.8.1/build/base/base.css
8 * - yui.yahooapis.com/2.8.1/build/base/base.css
9 * - camendesign.com/design/
9 * - camendesign.com/design/
10 * - praegnanz.de/weblog/htmlcssjs-kickstart
10 * - praegnanz.de/weblog/htmlcssjs-kickstart
11 */
11 */
12
12
13
13
14 /**
14 /**
15 * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline)
15 * html5doctor.com Reset Stylesheet (Eric Meyer's Reset Reloaded + HTML5 baseline)
16 * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark
16 * v1.6.1 2010-09-17 | Authors: Eric Meyer & Richard Clark
17 * html5doctor.com/html-5-reset-stylesheet/
17 * html5doctor.com/html-5-reset-stylesheet/
18 */
18 */
19
19
20 html, body, div, span, object, iframe,
20 html, body, div, span, object, iframe,
21 h1, h2, h3, h4, h5, h6, p, blockquote, pre,
21 h1, h2, h3, h4, h5, h6, p, blockquote, pre,
22 abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
22 abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp,
23 small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
23 small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li,
24 fieldset, form, label, legend,
24 fieldset, form, label, legend,
25 table, caption, tbody, tfoot, thead, tr, th, td,
25 table, caption, tbody, tfoot, thead, tr, th, td,
26 article, aside, canvas, details, figcaption, figure,
26 article, aside, canvas, details, figcaption, figure,
27 footer, header, hgroup, menu, nav, section, summary,
27 footer, header, hgroup, menu, nav, section, summary,
28 time, mark, audio, video {
28 time, mark, audio, video {
29 margin: 0;
29 margin: 0;
30 padding: 0;
30 padding: 0;
31 border: 0;
31 border: 0;
32 /* font-size: 100%;*/
32 /* font-size: 100%;*/
33 font: inherit;
33 font: inherit;
34 vertical-align: baseline;
34 vertical-align: baseline;
35 }
35 }
36
36
37 article, aside, details, figcaption, figure,
37 article, aside, details, figcaption, figure,
38 footer, header, hgroup, menu, nav, section {
38 footer, header, hgroup, menu, nav, section {
39 display: block;
39 display: block;
40 }
40 }
41
41
42 blockquote, q { quotes: none; }
42 blockquote, q { quotes: none; }
43
43
44 blockquote:before, blockquote:after,
44 blockquote:before, blockquote:after,
45 q:before, q:after { content: ""; content: none; }
45 q:before, q:after { content: ""; content: none; }
46
46
47 ins { background-color: #ff9; color: #000; text-decoration: none; }
47 ins { background-color: #ff9; color: #000; text-decoration: none; }
48
48
49 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
49 mark { background-color: #ff9; color: #000; font-style: italic; font-weight: bold; }
50
50
51 del { text-decoration: line-through; }
51 del { text-decoration: line-through; }
52
52
53 abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
53 abbr[title], dfn[title] { border-bottom: 1px dotted; cursor: help; }
54
54
55 table { border-collapse: collapse; border-spacing: 0; }
55 table { border-collapse: collapse; border-spacing: 0; }
56
56
57 hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
57 hr { display: block; height: 1px; border: 0; border-top: 1px solid #ccc; margin: 1em 0; padding: 0; }
58
58
59 input, select { vertical-align: middle; }
59 input, select { vertical-align: middle; }
60
60
61 body {
61 body {
62 background-color: white;
62 background-color: white;
63 /* This won't propagate to all children so we also set it below */
63 /* This won't propagate to all children so we also set it below */
64 font-size: 12pt;
64 font-size: 10pt;
65 /* This makes sure that the body covers the entire window and needs to
65 /* This makes sure that the body covers the entire window and needs to
66 be in a different element than the display: box in wrapper below */
66 be in a different element than the display: box in wrapper below */
67 position: absolute;
67 position: absolute;
68 left: 0px;
68 left: 0px;
69 right: 0px;
69 right: 0px;
70 top: 0px;
70 top: 0px;
71 bottom: 0px;
71 bottom: 0px;
72 overflow: hidden;
72 overflow: hidden;
73 }
73 }
74
74
75
75
76 span#ipython_notebook h1 {
76 span#ipython_notebook h1 {
77 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
77 font-family: Verdana, "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
78 font-size: 22pt;
78 font-size: 22pt;
79 height: 35px;
79 height: 35px;
80 padding: 5px;
80 padding: 5px;
81 margin: 0px;
81 margin: 0px;
82
82
83 }
83 }
84
84
85 div#tools {
86 font-size: 11pt;
87 }
88
89 span#kernel_status {
85 span#kernel_status {
90 position: absolute;
86 position: absolute;
91 top: 12%;
87 top: 12%;
92 right: 10px;
88 right: 10px;
93 font-weight: bold;
89 font-weight: bold;
94 }
90 }
95
91
96 .status_idle {
92 .status_idle {
97 color: gray;
93 color: gray;
98 }
94 }
99
95
100 .status_busy {
96 .status_busy {
101 color: red;
97 color: red;
102 }
98 }
103
99
104 .status_restarting {
100 .status_restarting {
105 color: black;
101 color: black;
106 }
102 }
107
103
108 div#notebook_app {
104 div#notebook_app {
109 width: 100%;
105 width: 100%;
110 position: relative;
106 position: relative;
111 }
107 }
112
108
113 div#left_panel {
109 div#left_panel {
114 overflow-y: auto;
110 overflow-y: auto;
115 top: 0px;
111 top: 0px;
116 left: 0px;
112 left: 0px;
117 margin: 0px;
113 margin: 0px;
118 padding: 0px;
114 padding: 0px;
119 position: absolute;
115 position: absolute;
120 }
116 }
121
117
122 div#left_panel_splitter {
118 div#left_panel_splitter {
123 width: 8px;
119 width: 8px;
124 top: 0px;
120 top: 0px;
125 left: 202px;
121 left: 202px;
126 margin: 0px;
122 margin: 0px;
127 padding: 0px;
123 padding: 0px;
128 position: absolute;
124 position: absolute;
129 }
125 }
130
126
131 div#notebook_panel {
127 div#notebook_panel {
132 /* The L margin will be set in the Javascript code*/
128 /* The L margin will be set in the Javascript code*/
133 margin: 0px 0px 0px 0px;
129 margin: 0px 0px 0px 0px;
134 padding: 0px;
130 padding: 0px;
135 }
131 }
136
132
137 div#notebook {
133 div#notebook {
138 overflow-y: scroll;
134 overflow-y: scroll;
139 overflow-x: auto;
135 overflow-x: auto;
140 width: 100%;
136 width: 100%;
141 padding: 0px 15px 15px 15px;
137 padding: 0px 15px 0px 15px;
142 margin: 0px
138 margin: 0px
143 background-color: white;
139 background-color: white;
144 font-size: 12pt;
140 font-size: 10pt;
145 }
141 }
146
142
147 div#pager_splitter {
143 div#pager_splitter {
148 height: 8px;
144 height: 8px;
149 }
145 }
150
146
151 div#pager {
147 div#pager {
148 padding: 15px;
152 overflow: auto;
149 overflow: auto;
153 }
150 }
154
151
155 .monospace-font {
152 .monospace-font {
156 font-family: monospace;
153 font-family: monospace;
157 font-size: 12pt;
154 font-size: 10pt;
158 }
155 }
159
156
160 div.cell {
157 div.cell {
161 width: 100%;
158 width: 100%;
162 padding: 5px;
159 padding: 5px;
163 /* This acts as a spacer between cells, that is outside the border */
160 /* This acts as a spacer between cells, that is outside the border */
164 margin: 15px 0px 15px 0px;
161 margin: 15px 0px 15px 0px;
165 }
162 }
166
163
167 div.code_cell {
164 div.code_cell {
168 background-color: white;
165 background-color: white;
169 }
166 }
170
167
171 div.prompt {
168 div.prompt {
172 width: 90px;
169 width: 90px;
173 padding: 0px;
170 padding: 0px;
174 margin: 0px;
171 margin: 0px;
175 }
172 }
176
173
177 div.input_prompt {
174 div.input_prompt {
178 color: navy;
175 color: navy;
179 }
176 }
180
177
181 div.output {
178 div.output {
182 /* This is a spacer between the input and output of each cell */
179 /* This is a spacer between the input and output of each cell */
183 margin-top: 15px;
180 margin-top: 15px;
184 }
181 }
185
182
186 div.output_prompt {
183 div.output_prompt {
187 color: darkred;
184 color: darkred;
188 }
185 }
189
186
190 div.output_area {
187 div.output_area {
191 text-align: left;
188 text-align: left;
192 color: black;
189 color: black;
193 }
190 }
194
191
195 div.output_latex {
192 div.output_latex {
196 /* Slightly bigger than the rest of the notebook */
193 /* Slightly bigger than the rest of the notebook */
197 font-size: 13pt;
194 font-size: 11pt;
198 }
195 }
199
196
200 div.output_png {
197 div.output_png {
201 }
198 }
202
199
203 div.text_cell {
200 div.text_cell {
204 background-color: white;
201 background-color: white;
205 }
202 }
206
203
207 textarea.text_cell_input {
204 textarea.text_cell_input {
208 /* Slightly bigger than the rest of the notebook */
205 /* Slightly bigger than the rest of the notebook */
209 font-size: 13pt;
206 font-size: 11pt;
210 outline: none;
207 outline: none;
211 resize: none;
208 resize: none;
212 width: inherit;
209 width: inherit;
213 border-style: none;
210 border-style: none;
214 padding: 0px;
211 padding: 0px;
215 margin: 0px;
212 margin: 0px;
216 color: black;
213 color: black;
217 }
214 }
218
215
219 div.text_cell_render {
216 div.text_cell_render {
220 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
217 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
221 /* Slightly bigger than the rest of the notebook */
218 /* Slightly bigger than the rest of the notebook */
222 font-size: 13pt;
219 font-size: 11pt;
223 outline: none;
220 outline: none;
224 resize: none;
221 resize: none;
225 width: inherit;
222 width: inherit;
226 border-style: none;
223 border-style: none;
227 padding: 5px;
224 padding: 5px;
228 color: black;
225 color: black;
229 }
226 }
230
227
231 div.text_cell_render em {font-style: italic;}
228 div.text_cell_render em {font-style: italic;}
232 div.text_cell_render strong {font-weight: bold;}
229 div.text_cell_render strong {font-weight: bold;}
233 div.text_cell_render u {text-decoration: underline;}
230 div.text_cell_render u {text-decoration: underline;}
234 div.text_cell_render :link { text-decoration: underline }
231 div.text_cell_render :link { text-decoration: underline }
235 div.text_cell_render :visited { text-decoration: underline }
232 div.text_cell_render :visited { text-decoration: underline }
236 div.text_cell_render h1 {font-size: 2.0em; margin: .67em 0; font-weight: bold;}
233 div.text_cell_render h1 {font-size: 2.0em; margin: .67em 0; font-weight: bold;}
237 div.text_cell_render h2 {font-size: 1.5em; margin: .75em 0; font-weight: bold;}
234 div.text_cell_render h2 {font-size: 1.5em; margin: .75em 0; font-weight: bold;}
238 div.text_cell_render h3 {font-size: 1.17em; margin: .83em 0; font-weight: bold;}
235 div.text_cell_render h3 {font-size: 1.17em; margin: .83em 0; font-weight: bold;}
239 div.text_cell_render h4 {margin: 1.12em 0; font-weight: bold;}
236 div.text_cell_render h4 {margin: 1.12em 0; font-weight: bold;}
240 div.text_cell_render h5 {font-size: .83em; margin: 1.5em 0; font-weight: bold;}
237 div.text_cell_render h5 {font-size: .83em; margin: 1.5em 0; font-weight: bold;}
241 div.text_cell_render h6 {font-size: .75em; margin: 1.67em 0; font-weight: bold;}
238 div.text_cell_render h6 {font-size: .75em; margin: 1.67em 0; font-weight: bold;}
242 div.text_cell_render ul {list-style:disc; margin-left: 40px;}
239 div.text_cell_render ul {list-style:disc; margin-left: 40px;}
243 div.text_cell_render ul ul {list-style:square; margin-left: 40px;}
240 div.text_cell_render ul ul {list-style:square; margin-left: 40px;}
244 div.text_cell_render ul ul ul {list-style:circle; margin-left: 40px;}
241 div.text_cell_render ul ul ul {list-style:circle; margin-left: 40px;}
245 div.text_cell_render ol {list-style:upper-roman; margin-left: 40px;}
242 div.text_cell_render ol {list-style:upper-roman; margin-left: 40px;}
246 div.text_cell_render ol ol {list-style:upper-alpha;}
243 div.text_cell_render ol ol {list-style:upper-alpha;}
247 div.text_cell_render ol ol ol {list-style:decimal;}
244 div.text_cell_render ol ol ol {list-style:decimal;}
248 div.text_cell_render ol ol ol ol {list-style:lower-alpha;}
245 div.text_cell_render ol ol ol ol {list-style:lower-alpha;}
249 div.text_cell_render ol ol ol ol ol {list-style:lower-roman;}
246 div.text_cell_render ol ol ol ol ol {list-style:lower-roman;}
250
247
251
248
@@ -1,571 +1,591 b''
1
1
2 //============================================================================
2 //============================================================================
3 // Notebook
3 // Notebook
4 //============================================================================
4 //============================================================================
5
5
6 var IPython = (function (IPython) {
6 var IPython = (function (IPython) {
7
7
8 var utils = IPython.utils;
8 var utils = IPython.utils;
9
9
10 var Notebook = function (selector) {
10 var Notebook = function (selector) {
11 this.element = $(selector);
11 this.element = $(selector);
12 this.element.scroll();
12 this.element.scroll();
13 this.element.data("notebook", this);
13 this.element.data("notebook", this);
14 this.next_prompt_number = 1;
14 this.next_prompt_number = 1;
15 this.kernel = null;
15 this.kernel = null;
16 this.msg_cell_map = {};
16 this.msg_cell_map = {};
17 this.filename = null;
17 this.filename = null;
18 this.notebook_load_re = /%notebook load/
18 this.notebook_load_re = /%notebook load/
19 this.notebook_save_re = /%notebook save/
19 this.notebook_save_re = /%notebook save/
20 this.notebook_filename_re = /(\w)+.ipynb/
20 this.notebook_filename_re = /(\w)+.ipynb/
21 this.style();
21 this.style();
22 this.create_elements();
22 this.bind_events();
23 this.bind_events();
23 this.start_kernel();
24 this.start_kernel();
24 };
25 };
25
26
26
27
27 Notebook.prototype.style = function () {
28 Notebook.prototype.style = function () {
28 $('div#notebook').addClass('border-box-sizing');
29 $('div#notebook').addClass('border-box-sizing');
29 };
30 };
30
31
31
32
33 Notebook.prototype.create_elements = function () {
34 // We add this end_space div to the end of the notebook div to:
35 // i) provide a margin between the last cell and the end of the notebook
36 // ii) to prevent the div from scrolling up when the last cell is being
37 // edited, but is too low on the page, which browsers will do automatically.
38 this.element.append($('<div class="end_space"></div>').height(50));
39 $('div#notebook').addClass('border-box-sizing');
40 };
41
42
32 Notebook.prototype.bind_events = function () {
43 Notebook.prototype.bind_events = function () {
33 var that = this;
44 var that = this;
34 $(document).keydown(function (event) {
45 $(document).keydown(function (event) {
35 // console.log(event);
46 // console.log(event);
36 if (event.which === 38) {
47 if (event.which === 38) {
37 var cell = that.selected_cell();
48 var cell = that.selected_cell();
38 if (cell.at_top()) {
49 if (cell.at_top()) {
39 event.preventDefault();
50 event.preventDefault();
40 that.select_prev();
51 that.select_prev();
41 };
52 };
42 } else if (event.which === 40) {
53 } else if (event.which === 40) {
43 var cell = that.selected_cell();
54 var cell = that.selected_cell();
44 if (cell.at_bottom()) {
55 if (cell.at_bottom()) {
45 event.preventDefault();
56 event.preventDefault();
46 that.select_next();
57 that.select_next();
47 };
58 };
48 } else if (event.which === 13 && event.shiftKey) {
59 } else if (event.which === 13 && event.shiftKey) {
49 // The focus is not quite working here.
60 // The focus is not quite working here.
50 var cell = that.selected_cell();
61 var cell = that.selected_cell();
51 var cell_index = that.find_cell_index(cell);
62 var cell_index = that.find_cell_index(cell);
52 // TODO: the logic here needs to be moved into appropriate
63 // TODO: the logic here needs to be moved into appropriate
53 // methods of Notebook.
64 // methods of Notebook.
54 if (cell instanceof IPython.CodeCell) {
65 if (cell instanceof IPython.CodeCell) {
55 event.preventDefault();
66 event.preventDefault();
56 cell.clear_output();
67 cell.clear_output();
57 var code = cell.get_code();
68 var code = cell.get_code();
58 if (that.notebook_load_re.test(code)) {
69 if (that.notebook_load_re.test(code)) {
59 var code_parts = code.split(' ');
70 var code_parts = code.split(' ');
60 if (code_parts.length === 3) {
71 if (code_parts.length === 3) {
61 that.load_notebook(code_parts[2]);
72 that.load_notebook(code_parts[2]);
62 };
73 };
63 } else if (that.notebook_save_re.test(code)) {
74 } else if (that.notebook_save_re.test(code)) {
64 var code_parts = code.split(' ');
75 var code_parts = code.split(' ');
65 if (code_parts.length === 3) {
76 if (code_parts.length === 3) {
66 that.save_notebook(code_parts[2]);
77 that.save_notebook(code_parts[2]);
67 } else {
78 } else {
68 that.save_notebook()
79 that.save_notebook()
69 };
80 };
70 } else {
81 } else {
71 var msg_id = that.kernel.execute(cell.get_code());
82 var msg_id = that.kernel.execute(cell.get_code());
72 that.msg_cell_map[msg_id] = cell.cell_id;
83 that.msg_cell_map[msg_id] = cell.cell_id;
73 };
84 };
74 } else if (cell instanceof IPython.TextCell) {
85 } else if (cell instanceof IPython.TextCell) {
75 event.preventDefault();
86 event.preventDefault();
76 cell.render();
87 cell.render();
77 }
88 }
78 if (cell_index === (that.ncells()-1)) {
89 if (cell_index === (that.ncells()-1)) {
79 that.insert_code_cell_after();
90 that.insert_code_cell_after();
91 // If we are adding a new cell at the end, scroll down to show it.
92 that.scroll_to_bottom();
80 } else {
93 } else {
81 that.select(cell_index+1);
94 that.select(cell_index+1);
82 };
95 };
83 };
96 };
84 });
97 });
85
98
86 this.element.bind('collapse_pager', function () {
99 this.element.bind('collapse_pager', function () {
87 var app_height = $('div#notebook_app').height(); // content height
100 var app_height = $('div#notebook_app').height(); // content height
88 var splitter_height = $('div#pager_splitter').outerHeight(true);
101 var splitter_height = $('div#pager_splitter').outerHeight(true);
89 var new_height = app_height - splitter_height;
102 var new_height = app_height - splitter_height;
90 that.element.animate({height : new_height + 'px'}, 'fast');
103 that.element.animate({height : new_height + 'px'}, 'fast');
91 });
104 });
92
105
93 this.element.bind('expand_pager', function () {
106 this.element.bind('expand_pager', function () {
94 var app_height = $('div#notebook_app').height(); // content height
107 var app_height = $('div#notebook_app').height(); // content height
95 var splitter_height = $('div#pager_splitter').outerHeight(true);
108 var splitter_height = $('div#pager_splitter').outerHeight(true);
96 var pager_height = $('div#pager').outerHeight(true);
109 var pager_height = $('div#pager').outerHeight(true);
97 var new_height = app_height - pager_height - splitter_height;
110 var new_height = app_height - pager_height - splitter_height;
98 that.element.animate({height : new_height + 'px'}, 'fast');
111 that.element.animate({height : new_height + 'px'}, 'fast');
99 });
112 });
100
113
101 this.element.bind('collapse_left_panel', function () {
114 this.element.bind('collapse_left_panel', function () {
102 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
115 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
103 var new_margin = splitter_width;
116 var new_margin = splitter_width;
104 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
117 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
105 });
118 });
106
119
107 this.element.bind('expand_left_panel', function () {
120 this.element.bind('expand_left_panel', function () {
108 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
121 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
109 var left_panel_width = IPython.left_panel.width;
122 var left_panel_width = IPython.left_panel.width;
110 var new_margin = splitter_width + left_panel_width;
123 var new_margin = splitter_width + left_panel_width;
111 console.log('expand', splitter_width, left_panel_width, new_margin);
124 console.log('expand', splitter_width, left_panel_width, new_margin);
112 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
125 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
113 });
126 });
114 };
127 };
115
128
116
129
130 Notebook.prototype.scroll_to_bottom = function () {
131 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 'slow');
132 };
133
117 // Cell indexing, retrieval, etc.
134 // Cell indexing, retrieval, etc.
118
135
119
136
120 Notebook.prototype.cell_elements = function () {
137 Notebook.prototype.cell_elements = function () {
121 return this.element.children("div.cell");
138 return this.element.children("div.cell");
122 }
139 }
123
140
124
141
125 Notebook.prototype.ncells = function (cell) {
142 Notebook.prototype.ncells = function (cell) {
126 return this.cell_elements().length;
143 return this.cell_elements().length;
127 }
144 }
128
145
129
146
130 // TODO: we are often calling cells as cells()[i], which we should optimize
147 // TODO: we are often calling cells as cells()[i], which we should optimize
131 // to cells(i) or a new method.
148 // to cells(i) or a new method.
132 Notebook.prototype.cells = function () {
149 Notebook.prototype.cells = function () {
133 return this.cell_elements().toArray().map(function (e) {
150 return this.cell_elements().toArray().map(function (e) {
134 return $(e).data("cell");
151 return $(e).data("cell");
135 });
152 });
136 }
153 }
137
154
138
155
139 Notebook.prototype.find_cell_index = function (cell) {
156 Notebook.prototype.find_cell_index = function (cell) {
140 var result = null;
157 var result = null;
141 this.cell_elements().filter(function (index) {
158 this.cell_elements().filter(function (index) {
142 if ($(this).data("cell") === cell) {
159 if ($(this).data("cell") === cell) {
143 result = index;
160 result = index;
144 };
161 };
145 });
162 });
146 return result;
163 return result;
147 };
164 };
148
165
149
166
150 Notebook.prototype.index_or_selected = function (index) {
167 Notebook.prototype.index_or_selected = function (index) {
151 return index || this.selected_index() || 0;
168 return index || this.selected_index() || 0;
152 }
169 }
153
170
154
171
155 Notebook.prototype.select = function (index) {
172 Notebook.prototype.select = function (index) {
156 if (index !== undefined && index >= 0 && index < this.ncells()) {
173 if (index !== undefined && index >= 0 && index < this.ncells()) {
157 if (this.selected_index() !== null) {
174 if (this.selected_index() !== null) {
158 this.selected_cell().unselect();
175 this.selected_cell().unselect();
159 };
176 };
160 this.cells()[index].select();
177 this.cells()[index].select();
178 if (index === (this.ncells()-1)) {
179 this.scroll_to_bottom();
180 };
161 };
181 };
162 return this;
182 return this;
163 };
183 };
164
184
165
185
166 Notebook.prototype.select_next = function () {
186 Notebook.prototype.select_next = function () {
167 var index = this.selected_index();
187 var index = this.selected_index();
168 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
188 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
169 this.select(index+1);
189 this.select(index+1);
170 };
190 };
171 return this;
191 return this;
172 };
192 };
173
193
174
194
175 Notebook.prototype.select_prev = function () {
195 Notebook.prototype.select_prev = function () {
176 var index = this.selected_index();
196 var index = this.selected_index();
177 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
197 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
178 this.select(index-1);
198 this.select(index-1);
179 };
199 };
180 return this;
200 return this;
181 };
201 };
182
202
183
203
184 Notebook.prototype.selected_index = function () {
204 Notebook.prototype.selected_index = function () {
185 var result = null;
205 var result = null;
186 this.cell_elements().filter(function (index) {
206 this.cell_elements().filter(function (index) {
187 if ($(this).data("cell").selected === true) {
207 if ($(this).data("cell").selected === true) {
188 result = index;
208 result = index;
189 };
209 };
190 });
210 });
191 return result;
211 return result;
192 };
212 };
193
213
194
214
195 Notebook.prototype.cell_for_msg = function (msg_id) {
215 Notebook.prototype.cell_for_msg = function (msg_id) {
196 var cell_id = this.msg_cell_map[msg_id];
216 var cell_id = this.msg_cell_map[msg_id];
197 var result = null;
217 var result = null;
198 this.cell_elements().filter(function (index) {
218 this.cell_elements().filter(function (index) {
199 cell = $(this).data("cell");
219 cell = $(this).data("cell");
200 if (cell.cell_id === cell_id) {
220 if (cell.cell_id === cell_id) {
201 result = cell;
221 result = cell;
202 };
222 };
203 });
223 });
204 return result;
224 return result;
205 };
225 };
206
226
207
227
208 Notebook.prototype.selected_cell = function () {
228 Notebook.prototype.selected_cell = function () {
209 return this.cell_elements().eq(this.selected_index()).data("cell");
229 return this.cell_elements().eq(this.selected_index()).data("cell");
210 }
230 }
211
231
212
232
213 // Cell insertion, deletion and moving.
233 // Cell insertion, deletion and moving.
214
234
215
235
216 Notebook.prototype.delete_cell = function (index) {
236 Notebook.prototype.delete_cell = function (index) {
217 var i = index || this.selected_index();
237 var i = index || this.selected_index();
218 if (i !== null && i >= 0 && i < this.ncells()) {
238 if (i !== null && i >= 0 && i < this.ncells()) {
219 this.cell_elements().eq(i).remove();
239 this.cell_elements().eq(i).remove();
220 if (i === (this.ncells())) {
240 if (i === (this.ncells())) {
221 this.select(i-1);
241 this.select(i-1);
222 } else {
242 } else {
223 this.select(i);
243 this.select(i);
224 };
244 };
225 };
245 };
226 return this;
246 return this;
227 };
247 };
228
248
229
249
230 Notebook.prototype.append_cell = function (cell) {
250 Notebook.prototype.append_cell = function (cell) {
231 this.element.append(cell.element);
251 this.element.find('div.end_space').before(cell.element);
232 return this;
252 return this;
233 };
253 };
234
254
235
255
236 Notebook.prototype.insert_cell_after = function (cell, index) {
256 Notebook.prototype.insert_cell_after = function (cell, index) {
237 var ncells = this.ncells();
257 var ncells = this.ncells();
238 if (ncells === 0) {
258 if (ncells === 0) {
239 this.append_cell(cell);
259 this.append_cell(cell);
240 return this;
260 return this;
241 };
261 };
242 if (index >= 0 && index < ncells) {
262 if (index >= 0 && index < ncells) {
243 this.cell_elements().eq(index).after(cell.element);
263 this.cell_elements().eq(index).after(cell.element);
244 };
264 };
245 return this
265 return this
246 };
266 };
247
267
248
268
249 Notebook.prototype.insert_cell_before = function (cell, index) {
269 Notebook.prototype.insert_cell_before = function (cell, index) {
250 var ncells = this.ncells();
270 var ncells = this.ncells();
251 if (ncells === 0) {
271 if (ncells === 0) {
252 this.append_cell(cell);
272 this.append_cell(cell);
253 return this;
273 return this;
254 };
274 };
255 if (index >= 0 && index < ncells) {
275 if (index >= 0 && index < ncells) {
256 this.cell_elements().eq(index).before(cell.element);
276 this.cell_elements().eq(index).before(cell.element);
257 };
277 };
258 return this;
278 return this;
259 };
279 };
260
280
261
281
262 Notebook.prototype.move_cell_up = function (index) {
282 Notebook.prototype.move_cell_up = function (index) {
263 var i = index || this.selected_index();
283 var i = index || this.selected_index();
264 if (i !== null && i < this.ncells() && i > 0) {
284 if (i !== null && i < this.ncells() && i > 0) {
265 var pivot = this.cell_elements().eq(i-1);
285 var pivot = this.cell_elements().eq(i-1);
266 var tomove = this.cell_elements().eq(i);
286 var tomove = this.cell_elements().eq(i);
267 if (pivot !== null && tomove !== null) {
287 if (pivot !== null && tomove !== null) {
268 tomove.detach();
288 tomove.detach();
269 pivot.before(tomove);
289 pivot.before(tomove);
270 this.select(i-1);
290 this.select(i-1);
271 };
291 };
272 };
292 };
273 return this;
293 return this;
274 }
294 }
275
295
276
296
277 Notebook.prototype.move_cell_down = function (index) {
297 Notebook.prototype.move_cell_down = function (index) {
278 var i = index || this.selected_index();
298 var i = index || this.selected_index();
279 if (i !== null && i < (this.ncells()-1) && i >= 0) {
299 if (i !== null && i < (this.ncells()-1) && i >= 0) {
280 var pivot = this.cell_elements().eq(i+1)
300 var pivot = this.cell_elements().eq(i+1)
281 var tomove = this.cell_elements().eq(i)
301 var tomove = this.cell_elements().eq(i)
282 if (pivot !== null && tomove !== null) {
302 if (pivot !== null && tomove !== null) {
283 tomove.detach();
303 tomove.detach();
284 pivot.after(tomove);
304 pivot.after(tomove);
285 this.select(i+1);
305 this.select(i+1);
286 };
306 };
287 };
307 };
288 return this;
308 return this;
289 }
309 }
290
310
291
311
292 Notebook.prototype.sort_cells = function () {
312 Notebook.prototype.sort_cells = function () {
293 var ncells = this.ncells();
313 var ncells = this.ncells();
294 var sindex = this.selected_index();
314 var sindex = this.selected_index();
295 var swapped;
315 var swapped;
296 do {
316 do {
297 swapped = false
317 swapped = false
298 for (var i=1; i<ncells; i++) {
318 for (var i=1; i<ncells; i++) {
299 current = this.cell_elements().eq(i).data("cell");
319 current = this.cell_elements().eq(i).data("cell");
300 previous = this.cell_elements().eq(i-1).data("cell");
320 previous = this.cell_elements().eq(i-1).data("cell");
301 if (previous.input_prompt_number > current.input_prompt_number) {
321 if (previous.input_prompt_number > current.input_prompt_number) {
302 this.move_cell_up(i);
322 this.move_cell_up(i);
303 swapped = true;
323 swapped = true;
304 };
324 };
305 };
325 };
306 } while (swapped);
326 } while (swapped);
307 this.select(sindex);
327 this.select(sindex);
308 return this;
328 return this;
309 };
329 };
310
330
311
331
312 Notebook.prototype.insert_code_cell_before = function (index) {
332 Notebook.prototype.insert_code_cell_before = function (index) {
313 // TODO: Bounds check for i
333 // TODO: Bounds check for i
314 var i = this.index_or_selected(index);
334 var i = this.index_or_selected(index);
315 var cell = new IPython.CodeCell(this);
335 var cell = new IPython.CodeCell(this);
316 cell.set_input_prompt(this.next_prompt_number);
336 cell.set_input_prompt(this.next_prompt_number);
317 this.next_prompt_number = this.next_prompt_number + 1;
337 this.next_prompt_number = this.next_prompt_number + 1;
318 this.insert_cell_before(cell, i);
338 this.insert_cell_before(cell, i);
319 this.select(this.find_cell_index(cell));
339 this.select(this.find_cell_index(cell));
320 return this;
340 return this;
321 }
341 }
322
342
323
343
324 Notebook.prototype.insert_code_cell_after = function (index) {
344 Notebook.prototype.insert_code_cell_after = function (index) {
325 // TODO: Bounds check for i
345 // TODO: Bounds check for i
326 var i = this.index_or_selected(index);
346 var i = this.index_or_selected(index);
327 var cell = new IPython.CodeCell(this);
347 var cell = new IPython.CodeCell(this);
328 cell.set_input_prompt(this.next_prompt_number);
348 cell.set_input_prompt(this.next_prompt_number);
329 this.next_prompt_number = this.next_prompt_number + 1;
349 this.next_prompt_number = this.next_prompt_number + 1;
330 this.insert_cell_after(cell, i);
350 this.insert_cell_after(cell, i);
331 this.select(this.find_cell_index(cell));
351 this.select(this.find_cell_index(cell));
332 return this;
352 return this;
333 }
353 }
334
354
335
355
336 Notebook.prototype.insert_text_cell_before = function (index) {
356 Notebook.prototype.insert_text_cell_before = function (index) {
337 // TODO: Bounds check for i
357 // TODO: Bounds check for i
338 var i = this.index_or_selected(index);
358 var i = this.index_or_selected(index);
339 var cell = new IPython.TextCell(this);
359 var cell = new IPython.TextCell(this);
340 cell.config_mathjax();
360 cell.config_mathjax();
341 this.insert_cell_before(cell, i);
361 this.insert_cell_before(cell, i);
342 this.select(this.find_cell_index(cell));
362 this.select(this.find_cell_index(cell));
343 return this;
363 return this;
344 }
364 }
345
365
346
366
347 Notebook.prototype.insert_text_cell_after = function (index) {
367 Notebook.prototype.insert_text_cell_after = function (index) {
348 // TODO: Bounds check for i
368 // TODO: Bounds check for i
349 var i = this.index_or_selected(index);
369 var i = this.index_or_selected(index);
350 var cell = new IPython.TextCell(this);
370 var cell = new IPython.TextCell(this);
351 cell.config_mathjax();
371 cell.config_mathjax();
352 this.insert_cell_after(cell, i);
372 this.insert_cell_after(cell, i);
353 this.select(this.find_cell_index(cell));
373 this.select(this.find_cell_index(cell));
354 return this;
374 return this;
355 }
375 }
356
376
357
377
358 Notebook.prototype.text_to_code = function (index) {
378 Notebook.prototype.text_to_code = function (index) {
359 // TODO: Bounds check for i
379 // TODO: Bounds check for i
360 var i = this.index_or_selected(index);
380 var i = this.index_or_selected(index);
361 var source_element = this.cell_elements().eq(i);
381 var source_element = this.cell_elements().eq(i);
362 var source_cell = source_element.data("cell");
382 var source_cell = source_element.data("cell");
363 if (source_cell instanceof IPython.TextCell) {
383 if (source_cell instanceof IPython.TextCell) {
364 this.insert_code_cell_after(i);
384 this.insert_code_cell_after(i);
365 var target_cell = this.cells()[i+1];
385 var target_cell = this.cells()[i+1];
366 target_cell.set_code(source_cell.get_text());
386 target_cell.set_code(source_cell.get_text());
367 source_element.remove();
387 source_element.remove();
368 };
388 };
369 };
389 };
370
390
371
391
372 Notebook.prototype.code_to_text = function (index) {
392 Notebook.prototype.code_to_text = function (index) {
373 // TODO: Bounds check for i
393 // TODO: Bounds check for i
374 var i = this.index_or_selected(index);
394 var i = this.index_or_selected(index);
375 var source_element = this.cell_elements().eq(i);
395 var source_element = this.cell_elements().eq(i);
376 var source_cell = source_element.data("cell");
396 var source_cell = source_element.data("cell");
377 if (source_cell instanceof IPython.CodeCell) {
397 if (source_cell instanceof IPython.CodeCell) {
378 this.insert_text_cell_after(i);
398 this.insert_text_cell_after(i);
379 var target_cell = this.cells()[i+1];
399 var target_cell = this.cells()[i+1];
380 var text = source_cell.get_code();
400 var text = source_cell.get_code();
381 if (text === "") {text = target_cell.placeholder;};
401 if (text === "") {text = target_cell.placeholder;};
382 target_cell.set_text(text);
402 target_cell.set_text(text);
383 source_element.remove();
403 source_element.remove();
384 target_cell.edit();
404 target_cell.edit();
385 };
405 };
386 };
406 };
387
407
388
408
389 // Cell collapsing
409 // Cell collapsing
390
410
391 Notebook.prototype.collapse = function (index) {
411 Notebook.prototype.collapse = function (index) {
392 var i = this.index_or_selected(index);
412 var i = this.index_or_selected(index);
393 this.cells()[i].collapse();
413 this.cells()[i].collapse();
394 };
414 };
395
415
396
416
397 Notebook.prototype.expand = function (index) {
417 Notebook.prototype.expand = function (index) {
398 var i = this.index_or_selected(index);
418 var i = this.index_or_selected(index);
399 this.cells()[i].expand();
419 this.cells()[i].expand();
400 };
420 };
401
421
402
422
403 // Kernel related things
423 // Kernel related things
404
424
405 Notebook.prototype.start_kernel = function () {
425 Notebook.prototype.start_kernel = function () {
406 this.kernel = new IPython.Kernel();
426 this.kernel = new IPython.Kernel();
407 this.kernel.start_kernel($.proxy(this.kernel_started, this));
427 this.kernel.start_kernel($.proxy(this.kernel_started, this));
408 };
428 };
409
429
410
430
411 Notebook.prototype.handle_shell_reply = function (e) {
431 Notebook.prototype.handle_shell_reply = function (e) {
412 reply = $.parseJSON(e.data);
432 reply = $.parseJSON(e.data);
413 var header = reply.header;
433 var header = reply.header;
414 var content = reply.content;
434 var content = reply.content;
415 var msg_type = header.msg_type;
435 var msg_type = header.msg_type;
416 console.log(reply);
436 console.log(reply);
417 var cell = this.cell_for_msg(reply.parent_header.msg_id);
437 var cell = this.cell_for_msg(reply.parent_header.msg_id);
418 if (msg_type === "execute_reply") {
438 if (msg_type === "execute_reply") {
419 cell.set_input_prompt(content.execution_count);
439 cell.set_input_prompt(content.execution_count);
420 };
440 };
421 var payload = content.payload || [];
441 var payload = content.payload || [];
422 this.handle_payload(content.payload);
442 this.handle_payload(content.payload);
423 };
443 };
424
444
425
445
426 Notebook.prototype.handle_payload = function (payload) {
446 Notebook.prototype.handle_payload = function (payload) {
427 var l = payload.length;
447 var l = payload.length;
428 if (l > 0) {
448 if (l > 0) {
429 IPython.pager.clear();
449 IPython.pager.clear();
430 IPython.pager.expand();
450 IPython.pager.expand();
431 };
451 };
432 for (var i=0; i<l; i++) {
452 for (var i=0; i<l; i++) {
433 IPython.pager.append_text(payload[i].text);
453 IPython.pager.append_text(payload[i].text);
434 };
454 };
435 };
455 };
436
456
437
457
438 Notebook.prototype.handle_iopub_reply = function (e) {
458 Notebook.prototype.handle_iopub_reply = function (e) {
439 reply = $.parseJSON(e.data);
459 reply = $.parseJSON(e.data);
440 var content = reply.content;
460 var content = reply.content;
441 // console.log(reply);
461 // console.log(reply);
442 var msg_type = reply.header.msg_type;
462 var msg_type = reply.header.msg_type;
443 var cell = this.cell_for_msg(reply.parent_header.msg_id);
463 var cell = this.cell_for_msg(reply.parent_header.msg_id);
444 if (msg_type === "stream") {
464 if (msg_type === "stream") {
445 cell.expand();
465 cell.expand();
446 cell.append_stream(content.data + "\n");
466 cell.append_stream(content.data + "\n");
447 } else if (msg_type === "display_data") {
467 } else if (msg_type === "display_data") {
448 cell.expand();
468 cell.expand();
449 cell.append_display_data(content.data);
469 cell.append_display_data(content.data);
450 } else if (msg_type === "pyout") {
470 } else if (msg_type === "pyout") {
451 cell.expand();
471 cell.expand();
452 cell.append_pyout(content.data, content.execution_count)
472 cell.append_pyout(content.data, content.execution_count)
453 } else if (msg_type === "pyerr") {
473 } else if (msg_type === "pyerr") {
454 cell.expand();
474 cell.expand();
455 cell.append_pyerr(content.ename, content.evalue, content.traceback);
475 cell.append_pyerr(content.ename, content.evalue, content.traceback);
456 } else if (msg_type === "status") {
476 } else if (msg_type === "status") {
457 if (content.execution_state === "busy") {
477 if (content.execution_state === "busy") {
458 this.kernel.status_busy();
478 this.kernel.status_busy();
459 } else if (content.execution_state === "idle") {
479 } else if (content.execution_state === "idle") {
460 this.kernel.status_idle();
480 this.kernel.status_idle();
461 };
481 };
462 }
482 }
463 };
483 };
464
484
465
485
466 Notebook.prototype.kernel_started = function () {
486 Notebook.prototype.kernel_started = function () {
467 console.log("Kernel started: ", this.kernel.kernel_id);
487 console.log("Kernel started: ", this.kernel.kernel_id);
468 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
488 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
469 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
489 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
470 };
490 };
471
491
472
492
473 // Persistance and loading
493 // Persistance and loading
474
494
475
495
476 Notebook.prototype.fromJSON = function (data) {
496 Notebook.prototype.fromJSON = function (data) {
477 var ncells = this.ncells();
497 var ncells = this.ncells();
478 for (var i=0; i<ncells; i++) {
498 for (var i=0; i<ncells; i++) {
479 // Always delete cell 0 as they get renumbered as they are deleted.
499 // Always delete cell 0 as they get renumbered as they are deleted.
480 this.delete_cell(0);
500 this.delete_cell(0);
481 };
501 };
482 var new_cells = data.cells;
502 var new_cells = data.cells;
483 ncells = new_cells.length;
503 ncells = new_cells.length;
484 var cell_data = null;
504 var cell_data = null;
485 for (var i=0; i<ncells; i++) {
505 for (var i=0; i<ncells; i++) {
486 cell_data = new_cells[i];
506 cell_data = new_cells[i];
487 if (cell_data.cell_type == 'code') {
507 if (cell_data.cell_type == 'code') {
488 this.insert_code_cell_after();
508 this.insert_code_cell_after();
489 this.selected_cell().fromJSON(cell_data);
509 this.selected_cell().fromJSON(cell_data);
490 } else if (cell_data.cell_type === 'text') {
510 } else if (cell_data.cell_type === 'text') {
491 this.insert_text_cell_after();
511 this.insert_text_cell_after();
492 this.selected_cell().fromJSON(cell_data);
512 this.selected_cell().fromJSON(cell_data);
493 };
513 };
494 };
514 };
495 };
515 };
496
516
497
517
498 Notebook.prototype.toJSON = function () {
518 Notebook.prototype.toJSON = function () {
499 var cells = this.cells();
519 var cells = this.cells();
500 var ncells = cells.length;
520 var ncells = cells.length;
501 cell_array = new Array(ncells);
521 cell_array = new Array(ncells);
502 for (var i=0; i<ncells; i++) {
522 for (var i=0; i<ncells; i++) {
503 cell_array[i] = cells[i].toJSON();
523 cell_array[i] = cells[i].toJSON();
504 };
524 };
505 json = {
525 json = {
506 cells : cell_array
526 cells : cell_array
507 };
527 };
508 return json
528 return json
509 };
529 };
510
530
511
531
512 Notebook.prototype.test_filename = function (filename) {
532 Notebook.prototype.test_filename = function (filename) {
513 if (this.notebook_filename_re.test(filename)) {
533 if (this.notebook_filename_re.test(filename)) {
514 return true;
534 return true;
515 } else {
535 } else {
516 var bad_filename = $('<div/>');
536 var bad_filename = $('<div/>');
517 bad_filename.html(
537 bad_filename.html(
518 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
538 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
519 );
539 );
520 bad_filename.dialog({title: 'Invalid filename', modal: true});
540 bad_filename.dialog({title: 'Invalid filename', modal: true});
521 return false;
541 return false;
522 };
542 };
523 };
543 };
524
544
525 Notebook.prototype.save_notebook = function (filename) {
545 Notebook.prototype.save_notebook = function (filename) {
526 this.filename = filename || this.filename || '';
546 this.filename = filename || this.filename || '';
527 if (this.filename === '') {
547 if (this.filename === '') {
528 var no_filename = $('<div/>');
548 var no_filename = $('<div/>');
529 no_filename.html(
549 no_filename.html(
530 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
550 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
531 );
551 );
532 no_filename.dialog({title: 'Missing filename', modal: true});
552 no_filename.dialog({title: 'Missing filename', modal: true});
533 return;
553 return;
534 }
554 }
535 if (!this.test_filename(this.filename)) {return;}
555 if (!this.test_filename(this.filename)) {return;}
536 var thedata = this.toJSON();
556 var thedata = this.toJSON();
537 var settings = {
557 var settings = {
538 processData : false,
558 processData : false,
539 cache : false,
559 cache : false,
540 type : "PUT",
560 type : "PUT",
541 data : JSON.stringify(thedata),
561 data : JSON.stringify(thedata),
542 success : function (data, status, xhr) {console.log(data);}
562 success : function (data, status, xhr) {console.log(data);}
543 };
563 };
544 $.ajax("/notebooks/" + this.filename, settings);
564 $.ajax("/notebooks/" + this.filename, settings);
545 };
565 };
546
566
547
567
548 Notebook.prototype.load_notebook = function (filename) {
568 Notebook.prototype.load_notebook = function (filename) {
549 if (!this.test_filename(filename)) {return;}
569 if (!this.test_filename(filename)) {return;}
550 var that = this;
570 var that = this;
551 // We do the call with settings so we can set cache to false.
571 // We do the call with settings so we can set cache to false.
552 var settings = {
572 var settings = {
553 processData : false,
573 processData : false,
554 cache : false,
574 cache : false,
555 type : "GET",
575 type : "GET",
556 dataType : "json",
576 dataType : "json",
557 success : function (data, status, xhr) {
577 success : function (data, status, xhr) {
558 that.fromJSON(data);
578 that.fromJSON(data);
559 that.filename = filename;
579 that.filename = filename;
560 that.kernel.restart();
580 that.kernel.restart();
561 }
581 }
562 };
582 };
563 $.ajax("/notebooks/" + filename, settings);
583 $.ajax("/notebooks/" + filename, settings);
564 }
584 }
565
585
566 IPython.Notebook = Notebook;
586 IPython.Notebook = Notebook;
567
587
568 return IPython;
588 return IPython;
569
589
570 }(IPython));
590 }(IPython));
571
591
General Comments 0
You need to be logged in to leave comments. Login now