##// END OF EJS Templates
Major refactoring of saving, notification....
Brian Granger -
Show More
@@ -0,0 +1,31
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // Events
10 //============================================================================
11
12 // Give us an object to bind all events to. This object should be created
13 // before all other objects so it exists when others register event handlers.
14 // To trigger an event handler:
15 // $([IPython.events]).trigger('event.Namespace);
16 // To handle it:
17 // $([IPython.events]).on('event.Namespace',function () {});
18
19 var IPython = (function (IPython) {
20
21 var utils = IPython.utils;
22
23 var Events = function () {};
24
25 IPython.Events = Events;
26 IPython.events = new Events();
27
28 return IPython;
29
30 }(IPython));
31
@@ -0,0 +1,102
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // Notification widget
10 //============================================================================
11
12 var IPython = (function (IPython) {
13
14 var utils = IPython.utils;
15
16
17 var NotificationWidget = function (selector) {
18 this.selector = selector;
19 this.timeout = null;
20 this.busy = false;
21 if (this.selector !== undefined) {
22 this.element = $(selector);
23 this.style();
24 this.bind_events();
25 }
26 };
27
28
29 NotificationWidget.prototype.style = function () {
30 this.element.addClass('ui-widget ui-widget-content ui-corner-all');
31 this.element.addClass('border-box-sizing');
32 };
33
34
35 NotificationWidget.prototype.bind_events = function () {
36 var that = this;
37 // Kernel events
38 $([IPython.events]).on('status_idle.Kernel',function () {
39 IPython.save_widget.update_document_title();
40 if (that.get_message() === 'Kernel busy') {
41 that.element.fadeOut(100, function () {
42 that.element.html('');
43 });
44 };
45 });
46 $([IPython.events]).on('status_busy.Kernel',function () {
47 window.document.title='(Busy) '+window.document.title;
48 that.set_message("Kernel busy");
49 });
50 $([IPython.events]).on('status_restarting.Kernel',function () {
51 that.set_message("Restarting kernel",500);
52 });
53 $([IPython.events]).on('status_interrupting.Kernel',function () {
54 that.set_message("Interrupting kernel",500);
55 });
56 // Notebook events
57 $([IPython.events]).on('notebook_loading.Notebook', function () {
58 that.set_message("Loading notebook",500);
59 });
60 $([IPython.events]).on('notebook_loaded.Notebook', function () {
61 that.set_message("Notebook loaded",500);
62 });
63 $([IPython.events]).on('notebook_saving.Notebook', function () {
64 that.set_message("Saving notebook",500);
65 });
66 $([IPython.events]).on('notebook_saved.Notebook', function () {
67 that.set_message("Notebook saved",500);
68 });
69 $([IPython.events]).on('notebook_save_failed.Notebook', function () {
70 that.set_message("Notebook save failed",500);
71 });
72 };
73
74
75 NotificationWidget.prototype.set_message = function (msg, timeout) {
76 var that = this;
77 this.element.html(msg);
78 this.element.fadeIn(100);
79 if (this.timeout !== null) {
80 clearTimeout(this.timeout);
81 this.timeout = null;
82 };
83 if (timeout !== undefined) {
84 this.timeout = setTimeout(function () {
85 that.element.fadeOut(100, function () {that.element.html('');});
86 that.timeout = null;
87 }, timeout)
88 };
89 };
90
91
92 NotificationWidget.prototype.get_message = function () {
93 return this.element.html();
94 };
95
96
97 IPython.NotificationWidget = NotificationWidget;
98
99 return IPython;
100
101 }(IPython));
102
@@ -1,393 +1,381
1 1 /**
2 2 * Primary styles
3 3 *
4 4 * Author: IPython Development Team
5 5 */
6 6
7 7
8 8 body {
9 9 background-color: white;
10 10 /* This makes sure that the body covers the entire window and needs to
11 11 be in a different element than the display: box in wrapper below */
12 12 position: absolute;
13 13 left: 0px;
14 14 right: 0px;
15 15 top: 0px;
16 16 bottom: 0px;
17 17 overflow: hidden;
18 18 }
19 19
20 20 span#save_widget {
21 21 padding: 5px;
22 22 margin: 0px 0px 0px 300px;
23 23 display:inline-block;
24 24 }
25 25
26 26 span#notebook_name {
27 27 height: 1em;
28 28 line-height: 1em;
29 29 padding: 3px;
30 30 border: none;
31 31 font-size: 146.5%;
32 32 }
33 33
34 34 #menubar {
35 35 /* Initially hidden to prevent FLOUC */
36 36 display: none;
37 37 }
38 38
39 39 .ui-menubar-item .ui-button .ui-button-text {
40 40 padding: 0.4em 1.0em;
41 41 font-size: 100%;
42 42 }
43 43
44 44 .ui-menu {
45 45 -moz-box-shadow: 0px 6px 10px -1px #adadad;
46 46 -webkit-box-shadow: 0px 6px 10px -1px #adadad;
47 47 box-shadow: 0px 6px 10px -1px #adadad;
48 48 }
49 49
50 50 .ui-menu .ui-menu-item a {
51 51 padding: 2px 1.6em;
52 52 }
53 53
54 54 .ui-menu hr {
55 55 margin: 0.3em 0;
56 56 }
57 57
58 #menubar_container {
59 position: relative;
60 }
61
62 #notification {
63 position: absolute;
64 right: 3px;
65 top: 3px;
66 height: 25px;
67 padding: 3px 6px;
68 z-index: 10;
69 }
70
58 71 #toolbar {
59 72 /* Initially hidden to prevent FLOUC */
60 73 display: none;
61 74 padding: 3px 15px;
62 75 }
63 76
64 77 #cell_type {
65 78 font-size: 85%;
66 79 }
67 80
68 81 span#quick_help_area {
69 82 position: static;
70 83 padding: 5px 0px;
71 84 margin: 0px 0px 0px 0px;
72 85 }
73 86
74 span#kernel_status {
75 position: absolute;
76 padding: 8px 5px 5px 5px;
77 right: 10px;
78 font-weight: bold;
79 }
80
81
82 .status_idle {
83 color: gray;
84 visibility: hidden;
85 }
86
87 .status_busy {
88 color: red;
89 }
90
91 .status_restarting {
92 color: black;
93 }
94
95 #kernel_persist {
96 float: right;
97 }
98
99 87 .help_string {
100 88 float: right;
101 89 width: 170px;
102 90 padding: 0px 5px;
103 91 text-align: left;
104 92 font-size: 85%;
105 93 }
106 94
107 95 .help_string_label {
108 96 float: right;
109 97 font-size: 85%;
110 98 }
111 99
112 100 div#notebook_panel {
113 101 margin: 0px 0px 0px 0px;
114 102 padding: 0px;
115 103 }
116 104
117 105 div#notebook {
118 106 overflow-y: scroll;
119 107 overflow-x: auto;
120 108 width: 100%;
121 109 /* This spaces the cell away from the edge of the notebook area */
122 110 padding: 5px 5px 15px 5px;
123 111 margin: 0px
124 112 background-color: white;
125 113 }
126 114
127 115 div#pager_splitter {
128 116 height: 8px;
129 117 }
130 118
131 119 div#pager {
132 120 padding: 15px;
133 121 overflow: auto;
134 122 display: none;
135 123 }
136 124
137 125 div.cell {
138 126 width: 100%;
139 127 padding: 5px 5px 5px 0px;
140 128 /* This acts as a spacer between cells, that is outside the border */
141 129 margin: 2px 0px 2px 0px;
142 130 }
143 131
144 132 div.code_cell {
145 133 background-color: white;
146 134 }
147 135 /* any special styling for code cells that are currently running goes here */
148 136 div.code_cell.running {
149 137 }
150 138
151 139 div.prompt {
152 140 /* This needs to be wide enough for 3 digit prompt numbers: In[100]: */
153 141 width: 11ex;
154 142 /* This 0.4em is tuned to match the padding on the CodeMirror editor. */
155 143 padding: 0.4em;
156 144 margin: 0px;
157 145 font-family: monospace;
158 146 text-align:right;
159 147 }
160 148
161 149 div.input {
162 150 page-break-inside: avoid;
163 151 }
164 152
165 153 /* input_area and input_prompt must match in top border and margin for alignment */
166 154 div.input_area {
167 155 color: black;
168 156 border: 1px solid #ddd;
169 157 border-radius: 3px;
170 158 background: #f7f7f7;
171 159 }
172 160
173 161 div.input_prompt {
174 162 color: navy;
175 163 border-top: 1px solid transparent;
176 164 }
177 165
178 166 div.output {
179 167 /* This is a spacer between the input and output of each cell */
180 168 margin-top: 5px;
181 169 }
182 170
183 171 div.output_prompt {
184 172 color: darkred;
185 173 }
186 174
187 175 /* This class is the outer container of all output sections. */
188 176 div.output_area {
189 177 padding: 0px;
190 178 page-break-inside: avoid;
191 179 }
192 180
193 181 /* This class is for the output subarea inside the output_area and after
194 182 the prompt div. */
195 183 div.output_subarea {
196 184 padding: 0.4em 6.1em 0.4em 0.4em;
197 185 }
198 186
199 187 /* The rest of the output_* classes are for special styling of the different
200 188 output types */
201 189
202 190 /* all text output has this class: */
203 191 div.output_text {
204 192 text-align: left;
205 193 color: black;
206 194 font-family: monospace;
207 195 }
208 196
209 197 /* stdout/stderr are 'text' as well as 'stream', but pyout/pyerr are *not* streams */
210 198 div.output_stream {
211 199 padding-top: 0.0em;
212 200 padding-bottom: 0.0em;
213 201 }
214 202 div.output_stdout {
215 203 }
216 204 div.output_stderr {
217 205 background: #fdd; /* very light red background for stderr */
218 206 }
219 207
220 208 div.output_latex {
221 209 text-align: left;
222 210 color: black;
223 211 }
224 212
225 213 div.output_html {
226 214 }
227 215
228 216 div.output_png {
229 217 }
230 218
231 219 div.output_jpeg {
232 220 }
233 221
234 222 div.text_cell {
235 223 background-color: white;
236 224 padding: 5px 5px 5px 5px;
237 225 }
238 226
239 227 div.text_cell_input {
240 228 color: black;
241 229 border: 1px solid #ddd;
242 230 border-radius: 3px;
243 231 background: #f7f7f7;
244 232 }
245 233
246 234 div.text_cell_render {
247 235 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
248 236 outline: none;
249 237 resize: none;
250 238 width: inherit;
251 239 border-style: none;
252 240 padding: 5px;
253 241 color: black;
254 242 }
255 243
256 244 .CodeMirror {
257 245 line-height: 1.231; /* Changed from 1em to our global default */
258 246 }
259 247
260 248 .CodeMirror-scroll {
261 249 height: auto; /* Changed to auto to autogrow */
262 250 /* The CodeMirror docs are a bit fuzzy on if overflow-y should be hidden or visible.*/
263 251 /* We have found that if it is visible, vertical scrollbars appear with font size changes.*/
264 252 overflow-y: hidden;
265 253 overflow-x: auto; /* Changed from auto to remove scrollbar */
266 254 }
267 255
268 256 /* CSS font colors for translated ANSI colors. */
269 257
270 258
271 259 .ansiblack {color: black;}
272 260 .ansired {color: darkred;}
273 261 .ansigreen {color: darkgreen;}
274 262 .ansiyellow {color: brown;}
275 263 .ansiblue {color: darkblue;}
276 264 .ansipurple {color: darkviolet;}
277 265 .ansicyan {color: steelblue;}
278 266 .ansigrey {color: grey;}
279 267 .ansibold {font-weight: bold;}
280 268
281 269 .completions , .tooltip {
282 270 position: absolute;
283 271 z-index: 10;
284 272 overflow: auto;
285 273 border: 1px solid black;
286 274 }
287 275
288 276 .completions select {
289 277 background: white;
290 278 outline: none;
291 279 border: none;
292 280 padding: 0px;
293 281 margin: 0px;
294 282 font-family: monospace;
295 283 }
296 284
297 285 @-moz-keyframes fadeIn {
298 286 from {opacity:0;}
299 287 to {opacity:1;}
300 288 }
301 289
302 290 @-webkit-keyframes fadeIn {
303 291 from {opacity:0;}
304 292 to {opacity:1;}
305 293 }
306 294
307 295 @keyframes fadeIn {
308 296 from {opacity:0;}
309 297 to {opacity:1;}
310 298 }
311 299
312 300 /*"close" "expand" and "Open in pager button" of
313 301 /* the tooltip*/
314 302 .tooltip a {
315 303 float:right;
316 304 }
317 305
318 306 /*properties of tooltip after "expand"*/
319 307 .bigtooltip {
320 308 height:30%;
321 309 }
322 310
323 311 /*properties of tooltip before "expand"*/
324 312 .smalltooltip {
325 313 text-overflow: ellipsis;
326 314 overflow: hidden;
327 315 height:15%;
328 316 }
329 317
330 318 .tooltip {
331 319 /*transition when "expand"ing tooltip */
332 320 -webkit-transition-property: height;
333 321 -webkit-transition-duration: 1s;
334 322 -moz-transition-property: height;
335 323 -moz-transition-duration: 1s;
336 324 transition-property: height;
337 325 transition-duration: 1s;
338 326 max-width:700px;
339 327 border-radius: 0px 10px 10px 10px;
340 328 box-shadow: 3px 3px 5px #999;
341 329 /*fade-in animation when inserted*/
342 330 -webkit-animation: fadeIn 200ms;
343 331 -moz-animation: fadeIn 200ms;
344 332 animation: fadeIn 200ms;
345 333 vertical-align: middle;
346 334 background: #FDFDD8;
347 335 outline: none;
348 336 padding: 3px;
349 337 margin: 0px;
350 338 font-family: monospace;
351 339 min-height:50px;
352 340 }
353 341
354 342 /*fixed part of the completion*/
355 343 .completions p b {
356 344 font-weight:bold;
357 345 }
358 346
359 347 .completions p {
360 348 background: #DDF;
361 349 /*outline: none;
362 350 padding: 0px;*/
363 351 border-bottom: black solid 1px;
364 352 padding: 1px;
365 353 font-family: monospace;
366 354 }
367 355
368 356 pre.dialog {
369 357 background-color: #f7f7f7;
370 358 border: 1px solid #ddd;
371 359 border-radius: 3px;
372 360 padding: 0.4em;
373 361 padding-left: 2em;
374 362 }
375 363
376 364 p.dialog {
377 365 padding : 0.2em;
378 366 }
379 367
380 368 .shortcut_key {
381 369 display: inline-block;
382 370 width: 15ex;
383 371 text-align: right;
384 372 font-family: monospace;
385 373 }
386 374
387 375 .shortcut_descr {
388 376 }
389 377
390 378 /* Word-wrap output correctly. This is the CSS3 spelling, though Firefox seems
391 379 to not honor it correctly. Webkit browsers (Chrome, rekonq, Safari) do.
392 380 */
393 381 pre, code, kbd, samp { white-space: pre-wrap; }
@@ -1,235 +1,235
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Kernel
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var Kernel = function () {
17 17 this.kernel_id = null;
18 18 this.shell_channel = null;
19 19 this.iopub_channel = null;
20 20 this.base_url = $('body').data('baseKernelUrl') + "kernels";
21 21 this.running = false;
22 22 this.username = "username";
23 23 this.session_id = utils.uuid();
24 24
25 25 if (typeof(WebSocket) !== 'undefined') {
26 26 this.WebSocket = WebSocket;
27 27 } else if (typeof(MozWebSocket) !== 'undefined') {
28 28 this.WebSocket = MozWebSocket;
29 29 } else {
30 30 alert('Your browser does not have WebSocket support, please try Chrome, Safari or Firefox ≥ 6. Firefox 4 and 5 are also supported by you have to enable WebSockets in about:config.');
31 31 };
32 32 };
33 33
34 34
35 35 Kernel.prototype.get_msg = function (msg_type, content) {
36 36 var msg = {
37 37 header : {
38 38 msg_id : utils.uuid(),
39 39 username : this.username,
40 40 session : this.session_id,
41 41 msg_type : msg_type
42 42 },
43 43 content : content,
44 44 parent_header : {}
45 45 };
46 46 return msg;
47 47 };
48 48
49 49 Kernel.prototype.start = function (notebook_id, callback) {
50 50 var that = this;
51 51 if (!this.running) {
52 52 var qs = $.param({notebook:notebook_id});
53 53 var url = this.base_url + '?' + qs;
54 54 $.post(url,
55 55 function (kernel_id) {
56 56 that._handle_start_kernel(kernel_id, callback);
57 57 },
58 58 'json'
59 59 );
60 60 };
61 61 };
62 62
63 63
64 64 Kernel.prototype.restart = function (callback) {
65 IPython.kernel_status_widget.status_restarting();
65 $([IPython.events]).trigger('status_restarting.Kernel');
66 66 var url = this.kernel_url + "/restart";
67 67 var that = this;
68 68 if (this.running) {
69 69 this.stop_channels();
70 70 $.post(url,
71 71 function (kernel_id) {
72 72 that._handle_start_kernel(kernel_id, callback);
73 73 },
74 74 'json'
75 75 );
76 76 };
77 77 };
78 78
79 79
80 80 Kernel.prototype._handle_start_kernel = function (json, callback) {
81 81 this.running = true;
82 82 this.kernel_id = json.kernel_id;
83 83 this.ws_url = json.ws_url;
84 84 this.kernel_url = this.base_url + "/" + this.kernel_id;
85 85 this.start_channels();
86 86 callback();
87 IPython.kernel_status_widget.status_idle();
88 87 };
89 88
90 89 Kernel.prototype._websocket_closed = function(ws_url, early){
91 90 var msg;
92 91 var parent_item = $('body');
93 92 if (early) {
94 msg = "Websocket connection to " + ws_url + " could not be established.<br/>" +
95 " You will NOT be able to run code.<br/>" +
93 msg = "Websocket connection to " + ws_url + " could not be established." +
94 " You will NOT be able to run code." +
96 95 " Your browser may not be compatible with the websocket version in the server," +
97 96 " or if the url does not look right, there could be an error in the" +
98 97 " server's configuration.";
99 98 } else {
100 msg = "Websocket connection closed unexpectedly.<br/>" +
99 msg = "Websocket connection closed unexpectedly." +
101 100 " The kernel will no longer be responsive.";
102 101 }
103 102 var dialog = $('<div/>');
104 103 dialog.html(msg);
105 104 parent_item.append(dialog);
106 105 dialog.dialog({
107 106 resizable: false,
108 107 modal: true,
109 108 title: "Websocket closed",
110 109 buttons : {
111 110 "Okay": function () {
112 111 $(this).dialog('close');
113 112 }
114 113 }
115 114 });
116 115
117 116 };
118 117
119 118 Kernel.prototype.start_channels = function () {
120 119 var that = this;
121 120 this.stop_channels();
122 121 var ws_url = this.ws_url + this.kernel_url;
123 122 console.log("Starting WS:", ws_url);
124 123 this.shell_channel = new this.WebSocket(ws_url + "/shell");
125 124 this.iopub_channel = new this.WebSocket(ws_url + "/iopub");
126 125 send_cookie = function(){
127 126 this.send(document.cookie);
128 127 };
129 128 var already_called_onclose = false; // only alert once
130 129 ws_closed_early = function(evt){
131 130 if (already_called_onclose){
132 131 return;
133 132 }
134 133 already_called_onclose = true;
135 134 if ( ! evt.wasClean ){
136 135 that._websocket_closed(ws_url, true);
137 136 }
138 137 };
139 138 ws_closed_late = function(evt){
140 139 if (already_called_onclose){
141 140 return;
142 141 }
143 142 already_called_onclose = true;
144 143 if ( ! evt.wasClean ){
145 144 that._websocket_closed(ws_url, false);
146 145 }
147 146 };
148 147 this.shell_channel.onopen = send_cookie;
149 148 this.shell_channel.onclose = ws_closed_early;
150 149 this.iopub_channel.onopen = send_cookie;
151 150 this.iopub_channel.onclose = ws_closed_early;
152 151 // switch from early-close to late-close message after 1s
153 152 setTimeout(function(){
154 153 that.shell_channel.onclose = ws_closed_late;
155 154 that.iopub_channel.onclose = ws_closed_late;
156 155 }, 1000);
157 156 };
158 157
159 158
160 159 Kernel.prototype.stop_channels = function () {
161 160 if (this.shell_channel !== null) {
162 161 this.shell_channel.onclose = function (evt) {};
163 162 this.shell_channel.close();
164 163 this.shell_channel = null;
165 164 };
166 165 if (this.iopub_channel !== null) {
167 166 this.iopub_channel.onclose = function (evt) {};
168 167 this.iopub_channel.close();
169 168 this.iopub_channel = null;
170 169 };
171 170 };
172 171
173 172 Kernel.prototype.object_info_request = function (objname) {
174 173 if(typeof(objname)!=null && objname!=null)
175 174 {
176 175 var content = {
177 176 oname : objname.toString(),
178 177 };
179 178 var msg = this.get_msg("object_info_request", content);
180 179 this.shell_channel.send(JSON.stringify(msg));
181 180 return msg.header.msg_id;
182 181 }
183 182 return;
184 183 }
185 184
186 185 Kernel.prototype.execute = function (code) {
187 186 var content = {
188 187 code : code,
189 188 silent : false,
190 189 user_variables : [],
191 190 user_expressions : {},
192 191 allow_stdin : false
193 192 };
194 193 var msg = this.get_msg("execute_request", content);
195 194 this.shell_channel.send(JSON.stringify(msg));
196 195 return msg.header.msg_id;
197 196 };
198 197
199 198
200 199 Kernel.prototype.complete = function (line, cursor_pos) {
201 200 var content = {
202 201 text : '',
203 202 line : line,
204 203 cursor_pos : cursor_pos
205 204 };
206 205 var msg = this.get_msg("complete_request", content);
207 206 this.shell_channel.send(JSON.stringify(msg));
208 207 return msg.header.msg_id;
209 208 };
210 209
211 210
212 211 Kernel.prototype.interrupt = function () {
213 212 if (this.running) {
213 $([IPython.events]).trigger('status_interrupting.Kernel');
214 214 $.post(this.kernel_url + "/interrupt");
215 215 };
216 216 };
217 217
218 218
219 219 Kernel.prototype.kill = function () {
220 220 if (this.running) {
221 221 this.running = false;
222 222 var settings = {
223 223 cache : false,
224 224 type : "DELETE"
225 225 };
226 226 $.ajax(this.kernel_url, settings);
227 227 };
228 228 };
229 229
230 230 IPython.Kernel = Kernel;
231 231
232 232 return IPython;
233 233
234 234 }(IPython));
235 235
@@ -1,177 +1,177
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // MenuBar
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var MenuBar = function (selector) {
15 15 this.selector = selector;
16 16 if (this.selector !== undefined) {
17 17 this.element = $(selector);
18 18 this.style();
19 19 this.bind_events();
20 20 }
21 21 };
22 22
23 23
24 24 MenuBar.prototype.style = function () {
25 25 $('ul#menus').menubar({
26 26 select : function (event, ui) {
27 27 // The selected cell loses focus when the menu is entered, so we
28 28 // re-select it upon selection.
29 29 var i = IPython.notebook.get_selected_index();
30 30 IPython.notebook.select(i);
31 31 }
32 32 });
33 33 };
34 34
35 35
36 36 MenuBar.prototype.bind_events = function () {
37 37 // File
38 38 this.element.find('#new_notebook').click(function () {
39 39 window.open($('body').data('baseProjectUrl')+'new');
40 40 });
41 41 this.element.find('#open_notebook').click(function () {
42 42 window.open($('body').data('baseProjectUrl'));
43 43 });
44 44 this.element.find('#rename_notebook').click(function () {
45 45 IPython.save_widget.rename_notebook();
46 46 });
47 47 this.element.find('#copy_notebook').click(function () {
48 var notebook_id = IPython.save_widget.get_notebook_id();
48 var notebook_id = IPython.notebook.get_notebook_id();
49 49 var url = $('body').data('baseProjectUrl') + notebook_id + '/copy';
50 50 window.open(url,'_newtab');
51 51 });
52 52 this.element.find('#save_notebook').click(function () {
53 IPython.save_widget.save_notebook();
53 IPython.notebook.save_notebook();
54 54 });
55 55 this.element.find('#download_ipynb').click(function () {
56 var notebook_id = IPython.save_widget.get_notebook_id();
56 var notebook_id = IPython.notebook.get_notebook_id();
57 57 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
58 58 notebook_id + '?format=json';
59 59 window.open(url,'_newtab');
60 60 });
61 61 this.element.find('#download_py').click(function () {
62 var notebook_id = IPython.save_widget.get_notebook_id();
62 var notebook_id = IPython.notebook.get_notebook_id();
63 63 var url = $('body').data('baseProjectUrl') + 'notebooks/' +
64 64 notebook_id + '?format=py';
65 65 window.open(url,'_newtab');
66 66 });
67 67 this.element.find('button#print_notebook').click(function () {
68 68 IPython.print_widget.print_notebook();
69 69 });
70 70 // Edit
71 71 this.element.find('#cut_cell').click(function () {
72 72 IPython.notebook.cut_cell();
73 73 });
74 74 this.element.find('#copy_cell').click(function () {
75 75 IPython.notebook.copy_cell();
76 76 });
77 77 this.element.find('#delete_cell').click(function () {
78 78 IPython.notebook.delete_cell();
79 79 });
80 80 this.element.find('#split_cell').click(function () {
81 81 IPython.notebook.split_cell();
82 82 });
83 83 this.element.find('#merge_cell_above').click(function () {
84 84 IPython.notebook.merge_cell_above();
85 85 });
86 86 this.element.find('#merge_cell_below').click(function () {
87 87 IPython.notebook.merge_cell_below();
88 88 });
89 89 this.element.find('#move_cell_up').click(function () {
90 90 IPython.notebook.move_cell_up();
91 91 });
92 92 this.element.find('#move_cell_down').click(function () {
93 93 IPython.notebook.move_cell_down();
94 94 });
95 95 this.element.find('#select_previous').click(function () {
96 96 IPython.notebook.select_prev();
97 97 });
98 98 this.element.find('#select_next').click(function () {
99 99 IPython.notebook.select_next();
100 100 });
101 101 // View
102 102 this.element.find('#toggle_header').click(function () {
103 103 $('div#header').toggle();
104 104 IPython.layout_manager.do_resize();
105 105 });
106 106 this.element.find('#toggle_toolbar').click(function () {
107 107 IPython.toolbar.toggle();
108 108 });
109 109 // Insert
110 110 this.element.find('#insert_cell_above').click(function () {
111 111 IPython.notebook.insert_cell_above('code');
112 112 });
113 113 this.element.find('#insert_cell_below').click(function () {
114 114 IPython.notebook.insert_cell_below('code');
115 115 });
116 116 // Cell
117 117 this.element.find('#run_cell').click(function () {
118 118 IPython.notebook.execute_selected_cell();
119 119 });
120 120 this.element.find('#run_cell_in_place').click(function () {
121 121 IPython.notebook.execute_selected_cell({terminal:true});
122 122 });
123 123 this.element.find('#run_all_cells').click(function () {
124 124 IPython.notebook.execute_all_cells();
125 125 });
126 126 this.element.find('#to_code').click(function () {
127 127 IPython.notebook.to_code();
128 128 });
129 129 this.element.find('#to_markdown').click(function () {
130 130 IPython.notebook.to_markdown();
131 131 });
132 132 this.element.find('#to_plaintext').click(function () {
133 133 IPython.notebook.to_plaintext();
134 134 });
135 135 this.element.find('#to_heading1').click(function () {
136 136 IPython.notebook.to_heading(undefined, 1);
137 137 });
138 138 this.element.find('#to_heading2').click(function () {
139 139 IPython.notebook.to_heading(undefined, 2);
140 140 });
141 141 this.element.find('#to_heading3').click(function () {
142 142 IPython.notebook.to_heading(undefined, 3);
143 143 });
144 144 this.element.find('#to_heading4').click(function () {
145 145 IPython.notebook.to_heading(undefined, 4);
146 146 });
147 147 this.element.find('#to_heading5').click(function () {
148 148 IPython.notebook.to_heading(undefined, 5);
149 149 });
150 150 this.element.find('#to_heading6').click(function () {
151 151 IPython.notebook.to_heading(undefined, 6);
152 152 });
153 153 this.element.find('#toggle_output').click(function () {
154 154 IPython.notebook.toggle_output();
155 155 });
156 156 this.element.find('#clear_all_output').click(function () {
157 157 IPython.notebook.clear_all_output();
158 158 });
159 159 // Kernel
160 160 this.element.find('#int_kernel').click(function () {
161 161 IPython.notebook.kernel.interrupt();
162 162 });
163 163 this.element.find('#restart_kernel').click(function () {
164 164 IPython.notebook.restart_kernel();
165 165 });
166 166 // Help
167 167 this.element.find('#keyboard_shortcuts').click(function () {
168 168 IPython.quick_help.show_keyboard_shortcuts();
169 169 });
170 170 };
171 171
172 172
173 173 IPython.MenuBar = MenuBar;
174 174
175 175 return IPython;
176 176
177 177 }(IPython));
@@ -1,1265 +1,1292
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notebook
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var Notebook = function (selector) {
17 17 this.read_only = IPython.read_only;
18 18 this.element = $(selector);
19 19 this.element.scroll();
20 20 this.element.data("notebook", this);
21 21 this.next_prompt_number = 1;
22 22 this.kernel = null;
23 23 this.clipboard = null;
24 24 this.paste_enabled = false;
25 25 this.dirty = false;
26 26 this.msg_cell_map = {};
27 27 this.metadata = {};
28 28 this.control_key_active = false;
29 this.notebook_id = null;
30 this.notebook_name = null;
31 this.notebook_name_blacklist_re = /[\/\\]/;
29 32 this.style();
30 33 this.create_elements();
31 34 this.bind_events();
32 35 this.set_tooltipontab(true);
33 36 this.set_smartcompleter(true);
34 37 this.set_timebeforetooltip(1200);
35 38 };
36 39
37 40
38 41 Notebook.prototype.style = function () {
39 42 $('div#notebook').addClass('border-box-sizing');
40 43 };
41 44
42 45
43 46 Notebook.prototype.create_elements = function () {
44 47 // We add this end_space div to the end of the notebook div to:
45 48 // i) provide a margin between the last cell and the end of the notebook
46 49 // ii) to prevent the div from scrolling up when the last cell is being
47 50 // edited, but is too low on the page, which browsers will do automatically.
48 51 var that = this;
49 52 var end_space = $('<div/>').addClass('end_space').height("30%");
50 53 end_space.dblclick(function (e) {
51 54 if (that.read_only) return;
52 55 var ncells = that.ncells();
53 56 that.insert_cell_below('code',ncells-1);
54 57 });
55 58 this.element.append(end_space);
56 59 $('div#notebook').addClass('border-box-sizing');
57 60 };
58 61
59 62
60 63 Notebook.prototype.bind_events = function () {
61 64 var that = this;
62 65 $(document).keydown(function (event) {
63 66 // console.log(event);
64 67 if (that.read_only) return true;
65 68
66 69 // Save (CTRL+S) or (AppleKey+S)
67 70 //metaKey = applekey on mac
68 71 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
69 IPython.save_widget.save_notebook();
72 that.save_notebook();
70 73 event.preventDefault();
71 74 return false;
72 75 } else if (event.which === 27) {
73 76 // Intercept escape at highest level to avoid closing
74 77 // websocket connection with firefox
75 78 event.preventDefault();
76 79 }
77 80 if (event.which === 38 && !event.shiftKey) {
78 81 var cell = that.get_selected_cell();
79 82 if (cell.at_top()) {
80 83 event.preventDefault();
81 84 that.select_prev();
82 85 };
83 86 } else if (event.which === 40 && !event.shiftKey) {
84 87 var cell = that.get_selected_cell();
85 88 if (cell.at_bottom()) {
86 89 event.preventDefault();
87 90 that.select_next();
88 91 };
89 92 } else if (event.which === 13 && event.shiftKey) {
90 93 that.execute_selected_cell();
91 94 return false;
92 95 } else if (event.which === 13 && event.ctrlKey) {
93 96 that.execute_selected_cell({terminal:true});
94 97 return false;
95 98 } else if (event.which === 77 && event.ctrlKey) {
96 99 that.control_key_active = true;
97 100 return false;
98 101 } else if (event.which === 88 && that.control_key_active) {
99 102 // Cut selected cell = x
100 103 that.cut_cell();
101 104 that.control_key_active = false;
102 105 return false;
103 106 } else if (event.which === 67 && that.control_key_active) {
104 107 // Copy selected cell = c
105 108 that.copy_cell();
106 109 that.control_key_active = false;
107 110 return false;
108 111 } else if (event.which === 86 && that.control_key_active) {
109 112 // Paste selected cell = v
110 113 that.paste_cell();
111 114 that.control_key_active = false;
112 115 return false;
113 116 } else if (event.which === 68 && that.control_key_active) {
114 117 // Delete selected cell = d
115 118 that.delete_cell();
116 119 that.control_key_active = false;
117 120 return false;
118 121 } else if (event.which === 65 && that.control_key_active) {
119 122 // Insert code cell above selected = a
120 123 that.insert_cell_above('code');
121 124 that.control_key_active = false;
122 125 return false;
123 126 } else if (event.which === 66 && that.control_key_active) {
124 127 // Insert code cell below selected = b
125 128 that.insert_cell_below('code');
126 129 that.control_key_active = false;
127 130 return false;
128 131 } else if (event.which === 89 && that.control_key_active) {
129 132 // To code = y
130 133 that.to_code();
131 134 that.control_key_active = false;
132 135 return false;
133 136 } else if (event.which === 77 && that.control_key_active) {
134 137 // To markdown = m
135 138 that.to_markdown();
136 139 that.control_key_active = false;
137 140 return false;
138 141 } else if (event.which === 84 && that.control_key_active) {
139 142 // To Plaintext = t
140 143 that.to_plaintext();
141 144 that.control_key_active = false;
142 145 return false;
143 146 } else if (event.which === 49 && that.control_key_active) {
144 147 // To Heading 1 = 1
145 148 that.to_heading(undefined, 1);
146 149 that.control_key_active = false;
147 150 return false;
148 151 } else if (event.which === 50 && that.control_key_active) {
149 152 // To Heading 2 = 2
150 153 that.to_heading(undefined, 2);
151 154 that.control_key_active = false;
152 155 return false;
153 156 } else if (event.which === 51 && that.control_key_active) {
154 157 // To Heading 3 = 3
155 158 that.to_heading(undefined, 3);
156 159 that.control_key_active = false;
157 160 return false;
158 161 } else if (event.which === 52 && that.control_key_active) {
159 162 // To Heading 4 = 4
160 163 that.to_heading(undefined, 4);
161 164 that.control_key_active = false;
162 165 return false;
163 166 } else if (event.which === 53 && that.control_key_active) {
164 167 // To Heading 5 = 5
165 168 that.to_heading(undefined, 5);
166 169 that.control_key_active = false;
167 170 return false;
168 171 } else if (event.which === 54 && that.control_key_active) {
169 172 // To Heading 6 = 6
170 173 that.to_heading(undefined, 6);
171 174 that.control_key_active = false;
172 175 return false;
173 176 } else if (event.which === 79 && that.control_key_active) {
174 177 // Toggle output = o
175 178 that.toggle_output();
176 179 that.control_key_active = false;
177 180 return false;
178 181 } else if (event.which === 83 && that.control_key_active) {
179 182 // Save notebook = s
180 IPython.save_widget.save_notebook();
183 that.save_notebook();
181 184 that.control_key_active = false;
182 185 return false;
183 186 } else if (event.which === 74 && that.control_key_active) {
184 187 // Move cell down = j
185 188 that.move_cell_down();
186 189 that.control_key_active = false;
187 190 return false;
188 191 } else if (event.which === 75 && that.control_key_active) {
189 192 // Move cell up = k
190 193 that.move_cell_up();
191 194 that.control_key_active = false;
192 195 return false;
193 196 } else if (event.which === 80 && that.control_key_active) {
194 197 // Select previous = p
195 198 that.select_prev();
196 199 that.control_key_active = false;
197 200 return false;
198 201 } else if (event.which === 78 && that.control_key_active) {
199 202 // Select next = n
200 203 that.select_next();
201 204 that.control_key_active = false;
202 205 return false;
203 206 } else if (event.which === 76 && that.control_key_active) {
204 207 // Toggle line numbers = l
205 208 that.cell_toggle_line_numbers();
206 209 that.control_key_active = false;
207 210 return false;
208 211 } else if (event.which === 73 && that.control_key_active) {
209 212 // Interrupt kernel = i
210 IPython.notebook.kernel.interrupt();
213 that.kernel.interrupt();
211 214 that.control_key_active = false;
212 215 return false;
213 216 } else if (event.which === 190 && that.control_key_active) {
214 217 // Restart kernel = . # matches qt console
215 IPython.notebook.restart_kernel();
218 that.restart_kernel();
216 219 that.control_key_active = false;
217 220 return false;
218 221 } else if (event.which === 72 && that.control_key_active) {
219 222 // Show keyboard shortcuts = h
220 223 IPython.quick_help.show_keyboard_shortcuts();
221 224 that.control_key_active = false;
222 225 return false;
223 226 } else if (that.control_key_active) {
224 227 that.control_key_active = false;
225 228 return true;
226 229 };
227 230 return true;
228 231 });
229 232
230 233 this.element.bind('collapse_pager', function () {
231 234 var app_height = $('div#main_app').height(); // content height
232 235 var splitter_height = $('div#pager_splitter').outerHeight(true);
233 236 var new_height = app_height - splitter_height;
234 237 that.element.animate({height : new_height + 'px'}, 'fast');
235 238 });
236 239
237 240 this.element.bind('expand_pager', function () {
238 241 var app_height = $('div#main_app').height(); // content height
239 242 var splitter_height = $('div#pager_splitter').outerHeight(true);
240 243 var pager_height = $('div#pager').outerHeight(true);
241 244 var new_height = app_height - pager_height - splitter_height;
242 245 that.element.animate({height : new_height + 'px'}, 'fast');
243 246 });
244 247
245 248 $(window).bind('beforeunload', function () {
246 249 // TODO: Make killing the kernel configurable.
247 250 var kill_kernel = false;
248 251 if (kill_kernel) {
249 252 that.kernel.kill();
250 253 }
251 254 if (that.dirty && ! that.read_only) {
252 255 return "You have unsaved changes that will be lost if you leave this page.";
253 256 };
254 257 // Null is the *only* return value that will make the browser not
255 258 // pop up the "don't leave" dialog.
256 259 return null;
257 260 });
258 261 };
259 262
260 263
261 264 Notebook.prototype.scroll_to_bottom = function () {
262 265 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
263 266 };
264 267
265 268
266 269 Notebook.prototype.scroll_to_top = function () {
267 270 this.element.animate({scrollTop:0}, 0);
268 271 };
269 272
270 273
271 274 // Cell indexing, retrieval, etc.
272 275
273 276 Notebook.prototype.get_cell_elements = function () {
274 277 return this.element.children("div.cell");
275 278 };
276 279
277 280
278 281 Notebook.prototype.get_cell_element = function (index) {
279 282 var result = null;
280 283 var e = this.get_cell_elements().eq(index);
281 284 if (e.length !== 0) {
282 285 result = e;
283 286 }
284 287 return result;
285 288 };
286 289
287 290
288 291 Notebook.prototype.ncells = function (cell) {
289 292 return this.get_cell_elements().length;
290 293 };
291 294
292 295
293 296 // TODO: we are often calling cells as cells()[i], which we should optimize
294 297 // to cells(i) or a new method.
295 298 Notebook.prototype.get_cells = function () {
296 299 return this.get_cell_elements().toArray().map(function (e) {
297 300 return $(e).data("cell");
298 301 });
299 302 };
300 303
301 304
302 305 Notebook.prototype.get_cell = function (index) {
303 306 var result = null;
304 307 var ce = this.get_cell_element(index);
305 308 if (ce !== null) {
306 309 result = ce.data('cell');
307 310 }
308 311 return result;
309 312 }
310 313
311 314
312 315 Notebook.prototype.get_next_cell = function (cell) {
313 316 var result = null;
314 317 var index = this.find_cell_index(cell);
315 318 if (index !== null && index < this.ncells()) {
316 319 result = this.get_cell(index+1);
317 320 }
318 321 return result;
319 322 }
320 323
321 324
322 325 Notebook.prototype.get_prev_cell = function (cell) {
323 326 var result = null;
324 327 var index = this.find_cell_index(cell);
325 328 if (index !== null && index > 1) {
326 329 result = this.get_cell(index-1);
327 330 }
328 331 return result;
329 332 }
330 333
331 334 Notebook.prototype.find_cell_index = function (cell) {
332 335 var result = null;
333 336 this.get_cell_elements().filter(function (index) {
334 337 if ($(this).data("cell") === cell) {
335 338 result = index;
336 339 };
337 340 });
338 341 return result;
339 342 };
340 343
341 344
342 345 Notebook.prototype.index_or_selected = function (index) {
343 346 var i;
344 347 if (index === undefined || index === null) {
345 348 i = this.get_selected_index();
346 349 if (i === null) {
347 350 i = 0;
348 351 }
349 352 } else {
350 353 i = index;
351 354 }
352 355 return i;
353 356 };
354 357
355 358
356 359 Notebook.prototype.get_selected_cell = function () {
357 360 var index = this.get_selected_index();
358 361 return this.get_cell(index);
359 362 };
360 363
361 364
362 365 Notebook.prototype.is_valid_cell_index = function (index) {
363 366 if (index !== null && index >= 0 && index < this.ncells()) {
364 367 return true;
365 368 } else {
366 369 return false;
367 370 };
368 371 }
369 372
370 373 Notebook.prototype.get_selected_index = function () {
371 374 var result = null;
372 375 this.get_cell_elements().filter(function (index) {
373 376 if ($(this).data("cell").selected === true) {
374 377 result = index;
375 378 };
376 379 });
377 380 return result;
378 381 };
379 382
380 383
381 384 Notebook.prototype.cell_for_msg = function (msg_id) {
382 385 var cell_id = this.msg_cell_map[msg_id];
383 386 var result = null;
384 387 this.get_cell_elements().filter(function (index) {
385 388 cell = $(this).data("cell");
386 389 if (cell.cell_id === cell_id) {
387 390 result = cell;
388 391 };
389 392 });
390 393 return result;
391 394 };
392 395
393 396
394 397 // Cell selection.
395 398
396 399 Notebook.prototype.select = function (index) {
397 400 if (index !== undefined && index >= 0 && index < this.ncells()) {
398 401 sindex = this.get_selected_index()
399 402 if (sindex !== null && index !== sindex) {
400 403 this.get_cell(sindex).unselect();
401 404 };
402 405 var cell = this.get_cell(index)
403 406 cell.select();
404 407 if (cell.cell_type === 'heading') {
405 IPython.toolbar.set_cell_type(cell.cell_type+cell.level);
408 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
409 {'cell_type':cell.cell_type,level:cell.level}
410 );
406 411 } else {
407 IPython.toolbar.set_cell_type(cell.cell_type)
408 }
412 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
413 {'cell_type':cell.cell_type}
414 );
415 };
409 416 };
410 417 return this;
411 418 };
412 419
413 420
414 421 Notebook.prototype.select_next = function () {
415 422 var index = this.get_selected_index();
416 423 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
417 424 this.select(index+1);
418 425 };
419 426 return this;
420 427 };
421 428
422 429
423 430 Notebook.prototype.select_prev = function () {
424 431 var index = this.get_selected_index();
425 432 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
426 433 this.select(index-1);
427 434 };
428 435 return this;
429 436 };
430 437
431 438
432 439 // Cell movement
433 440
434 441 Notebook.prototype.move_cell_up = function (index) {
435 442 var i = this.index_or_selected();
436 443 if (i !== null && i < this.ncells() && i > 0) {
437 444 var pivot = this.get_cell_element(i-1);
438 445 var tomove = this.get_cell_element(i);
439 446 if (pivot !== null && tomove !== null) {
440 447 tomove.detach();
441 448 pivot.before(tomove);
442 449 this.select(i-1);
443 450 };
444 451 };
445 452 this.dirty = true;
446 453 return this;
447 454 };
448 455
449 456
450 457 Notebook.prototype.move_cell_down = function (index) {
451 458 var i = this.index_or_selected();
452 459 if (i !== null && i < (this.ncells()-1) && i >= 0) {
453 460 var pivot = this.get_cell_element(i+1);
454 461 var tomove = this.get_cell_element(i);
455 462 if (pivot !== null && tomove !== null) {
456 463 tomove.detach();
457 464 pivot.after(tomove);
458 465 this.select(i+1);
459 466 };
460 467 };
461 468 this.dirty = true;
462 469 return this;
463 470 };
464 471
465 472
466 473 Notebook.prototype.sort_cells = function () {
467 474 // This is not working right now. Calling this will actually crash
468 475 // the browser. I think there is an infinite loop in here...
469 476 var ncells = this.ncells();
470 477 var sindex = this.get_selected_index();
471 478 var swapped;
472 479 do {
473 480 swapped = false;
474 481 for (var i=1; i<ncells; i++) {
475 482 current = this.get_cell(i);
476 483 previous = this.get_cell(i-1);
477 484 if (previous.input_prompt_number > current.input_prompt_number) {
478 485 this.move_cell_up(i);
479 486 swapped = true;
480 487 };
481 488 };
482 489 } while (swapped);
483 490 this.select(sindex);
484 491 return this;
485 492 };
486 493
487 494 // Insertion, deletion.
488 495
489 496 Notebook.prototype.delete_cell = function (index) {
490 497 var i = this.index_or_selected(index);
491 498 if (this.is_valid_cell_index(i)) {
492 499 var ce = this.get_cell_element(i);
493 500 ce.remove();
494 501 if (i === (this.ncells())) {
495 502 this.select(i-1);
496 503 } else {
497 504 this.select(i);
498 505 };
499 506 this.dirty = true;
500 507 };
501 508 return this;
502 509 };
503 510
504 511
505 512 Notebook.prototype.insert_cell_below = function (type, index) {
506 513 // type = ('code','html','markdown')
507 514 // index = cell index or undefined to insert below selected
508 515 index = this.index_or_selected(index);
509 516 var cell = null;
510 517 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
511 518 if (type === 'code') {
512 519 cell = new IPython.CodeCell(this);
513 520 cell.set_input_prompt();
514 521 } else if (type === 'markdown') {
515 522 cell = new IPython.MarkdownCell(this);
516 523 } else if (type === 'html') {
517 524 cell = new IPython.HTMLCell(this);
518 525 } else if (type === 'plaintext') {
519 526 cell = new IPython.PlaintextCell(this);
520 527 } else if (type === 'heading') {
521 528 cell = new IPython.HeadingCell(this);
522 529 };
523 530 if (cell !== null) {
524 531 if (this.ncells() === 0) {
525 532 this.element.find('div.end_space').before(cell.element);
526 533 } else if (this.is_valid_cell_index(index)) {
527 534 this.get_cell_element(index).after(cell.element);
528 535 };
529 536 cell.render();
530 537 this.select(this.find_cell_index(cell));
531 538 this.dirty = true;
532 539 return cell;
533 540 };
534 541 };
535 542 return cell;
536 543 };
537 544
538 545
539 546 Notebook.prototype.insert_cell_above = function (type, index) {
540 547 // type = ('code','html','markdown')
541 548 // index = cell index or undefined to insert above selected
542 549 index = this.index_or_selected(index);
543 550 var cell = null;
544 551 if (this.ncells() === 0 || this.is_valid_cell_index(index)) {
545 552 if (type === 'code') {
546 553 cell = new IPython.CodeCell(this);
547 554 cell.set_input_prompt();
548 555 } else if (type === 'markdown') {
549 556 cell = new IPython.MarkdownCell(this);
550 557 } else if (type === 'html') {
551 558 cell = new IPython.HTMLCell(this);
552 559 } else if (type === 'plaintext') {
553 560 cell = new IPython.PlaintextCell(this);
554 561 } else if (type === 'heading') {
555 562 cell = new IPython.HeadingCell(this);
556 563 };
557 564 if (cell !== null) {
558 565 if (this.ncells() === 0) {
559 566 this.element.find('div.end_space').before(cell.element);
560 567 } else if (this.is_valid_cell_index(index)) {
561 568 this.get_cell_element(index).before(cell.element);
562 569 };
563 570 cell.render();
564 571 this.select(this.find_cell_index(cell));
565 572 this.dirty = true;
566 573 return cell;
567 574 };
568 575 };
569 576 return cell;
570 577 };
571 578
572 579
573 580 Notebook.prototype.to_code = function (index) {
574 581 var i = this.index_or_selected(index);
575 582 if (this.is_valid_cell_index(i)) {
576 583 var source_element = this.get_cell_element(i);
577 584 var source_cell = source_element.data("cell");
578 585 if (!(source_cell instanceof IPython.CodeCell)) {
579 586 target_cell = this.insert_cell_below('code',i);
580 587 var text = source_cell.get_text();
581 588 if (text === source_cell.placeholder) {
582 589 text = '';
583 590 }
584 591 target_cell.set_text(text);
585 592 source_element.remove();
586 593 this.dirty = true;
587 594 };
588 595 };
589 596 };
590 597
591 598
592 599 Notebook.prototype.to_markdown = function (index) {
593 600 var i = this.index_or_selected(index);
594 601 if (this.is_valid_cell_index(i)) {
595 602 var source_element = this.get_cell_element(i);
596 603 var source_cell = source_element.data("cell");
597 604 if (!(source_cell instanceof IPython.MarkdownCell)) {
598 605 target_cell = this.insert_cell_below('markdown',i);
599 606 var text = source_cell.get_text();
600 607 if (text === source_cell.placeholder) {
601 608 text = '';
602 609 };
603 610 // The edit must come before the set_text.
604 611 target_cell.edit();
605 612 target_cell.set_text(text);
606 613 source_element.remove();
607 614 this.dirty = true;
608 615 };
609 616 };
610 617 };
611 618
612 619
613 620 Notebook.prototype.to_html = function (index) {
614 621 var i = this.index_or_selected(index);
615 622 if (this.is_valid_cell_index(i)) {
616 623 var source_element = this.get_cell_element(i);
617 624 var source_cell = source_element.data("cell");
618 625 var target_cell = null;
619 626 if (!(source_cell instanceof IPython.HTMLCell)) {
620 627 target_cell = this.insert_cell_below('html',i);
621 628 var text = source_cell.get_text();
622 629 if (text === source_cell.placeholder) {
623 630 text = '';
624 631 };
625 632 // The edit must come before the set_text.
626 633 target_cell.edit();
627 634 target_cell.set_text(text);
628 635 source_element.remove();
629 636 this.dirty = true;
630 637 };
631 638 };
632 639 };
633 640
634 641
635 642 Notebook.prototype.to_plaintext = function (index) {
636 643 var i = this.index_or_selected(index);
637 644 if (this.is_valid_cell_index(i)) {
638 645 var source_element = this.get_cell_element(i);
639 646 var source_cell = source_element.data("cell");
640 647 var target_cell = null;
641 648 if (!(source_cell instanceof IPython.PlaintextCell)) {
642 649 target_cell = this.insert_cell_below('plaintext',i);
643 650 var text = source_cell.get_text();
644 651 if (text === source_cell.placeholder) {
645 652 text = '';
646 653 };
647 654 // The edit must come before the set_text.
648 655 target_cell.edit();
649 656 target_cell.set_text(text);
650 657 source_element.remove();
651 658 this.dirty = true;
652 659 };
653 660 };
654 661 };
655 662
656 663
657 664 Notebook.prototype.to_heading = function (index, level) {
658 665 level = level || 1;
659 666 var i = this.index_or_selected(index);
660 667 if (this.is_valid_cell_index(i)) {
661 668 var source_element = this.get_cell_element(i);
662 669 var source_cell = source_element.data("cell");
663 670 var target_cell = null;
664 671 if (source_cell instanceof IPython.HeadingCell) {
665 672 source_cell.set_level(level);
666 673 } else {
667 674 target_cell = this.insert_cell_below('heading',i);
668 675 var text = source_cell.get_text();
669 676 if (text === source_cell.placeholder) {
670 677 text = '';
671 678 };
672 679 // The edit must come before the set_text.
673 680 target_cell.set_level(level);
674 681 target_cell.edit();
675 682 target_cell.set_text(text);
676 683 source_element.remove();
677 684 this.dirty = true;
678 685 };
679 IPython.toolbar.set_cell_type("heading"+level);
686 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
687 {'cell_type':'heading',level:level}
688 );
680 689 };
681 690 };
682 691
683 692
684 693 // Cut/Copy/Paste
685 694
686 695 Notebook.prototype.enable_paste = function () {
687 696 var that = this;
688 697 if (!this.paste_enabled) {
689 698 $('#paste_cell').removeClass('ui-state-disabled')
690 699 .on('click', function () {that.paste_cell();});
691 700 $('#paste_cell_above').removeClass('ui-state-disabled')
692 701 .on('click', function () {that.paste_cell_above();});
693 702 $('#paste_cell_below').removeClass('ui-state-disabled')
694 703 .on('click', function () {that.paste_cell_below();});
695 704 this.paste_enabled = true;
696 705 };
697 706 };
698 707
699 708
700 709 Notebook.prototype.disable_paste = function () {
701 710 if (this.paste_enabled) {
702 711 $('#paste_cell').addClass('ui-state-disabled').off('click');
703 712 $('#paste_cell_above').addClass('ui-state-disabled').off('click');
704 713 $('#paste_cell_below').addClass('ui-state-disabled').off('click');
705 714 this.paste_enabled = false;
706 715 };
707 716 };
708 717
709 718
710 719 Notebook.prototype.cut_cell = function () {
711 720 this.copy_cell();
712 721 this.delete_cell();
713 722 }
714 723
715 724 Notebook.prototype.copy_cell = function () {
716 725 var cell = this.get_selected_cell();
717 726 this.clipboard = cell.toJSON();
718 727 this.enable_paste();
719 728 };
720 729
721 730
722 731 Notebook.prototype.paste_cell = function () {
723 732 if (this.clipboard !== null && this.paste_enabled) {
724 733 var cell_data = this.clipboard;
725 734 var new_cell = this.insert_cell_above(cell_data.cell_type);
726 735 new_cell.fromJSON(cell_data);
727 736 old_cell = this.get_next_cell(new_cell);
728 737 this.delete_cell(this.find_cell_index(old_cell));
729 738 this.select(this.find_cell_index(new_cell));
730 739 };
731 740 };
732 741
733 742
734 743 Notebook.prototype.paste_cell_above = function () {
735 744 if (this.clipboard !== null && this.paste_enabled) {
736 745 var cell_data = this.clipboard;
737 746 var new_cell = this.insert_cell_above(cell_data.cell_type);
738 747 new_cell.fromJSON(cell_data);
739 748 };
740 749 };
741 750
742 751
743 752 Notebook.prototype.paste_cell_below = function () {
744 753 if (this.clipboard !== null && this.paste_enabled) {
745 754 var cell_data = this.clipboard;
746 755 var new_cell = this.insert_cell_below(cell_data.cell_type);
747 756 new_cell.fromJSON(cell_data);
748 757 };
749 758 };
750 759
751 760
752 761 // Split/merge
753 762
754 763 Notebook.prototype.split_cell = function () {
755 764 // Todo: implement spliting for other cell types.
756 765 var cell = this.get_selected_cell();
757 766 if (cell.is_splittable()) {
758 767 texta = cell.get_pre_cursor();
759 768 textb = cell.get_post_cursor();
760 769 if (cell instanceof IPython.CodeCell) {
761 770 cell.set_text(texta);
762 771 var new_cell = this.insert_cell_below('code');
763 772 new_cell.set_text(textb);
764 773 } else if (cell instanceof IPython.MarkdownCell) {
765 774 cell.set_text(texta);
766 775 cell.render();
767 776 var new_cell = this.insert_cell_below('markdown');
768 777 new_cell.edit(); // editor must be visible to call set_text
769 778 new_cell.set_text(textb);
770 779 new_cell.render();
771 780 } else if (cell instanceof IPython.HTMLCell) {
772 781 cell.set_text(texta);
773 782 cell.render();
774 783 var new_cell = this.insert_cell_below('html');
775 784 new_cell.edit(); // editor must be visible to call set_text
776 785 new_cell.set_text(textb);
777 786 new_cell.render();
778 787 };
779 788 };
780 789 };
781 790
782 791
783 792 Notebook.prototype.merge_cell_above = function () {
784 793 var index = this.get_selected_index();
785 794 var cell = this.get_cell(index);
786 795 if (index > 0) {
787 796 upper_cell = this.get_cell(index-1);
788 797 upper_text = upper_cell.get_text();
789 798 text = cell.get_text();
790 799 if (cell instanceof IPython.CodeCell) {
791 800 cell.set_text(upper_text+'\n'+text);
792 801 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
793 802 cell.edit();
794 803 cell.set_text(upper_text+'\n'+text);
795 804 cell.render();
796 805 };
797 806 this.delete_cell(index-1);
798 807 this.select(this.find_cell_index(cell));
799 808 };
800 809 };
801 810
802 811
803 812 Notebook.prototype.merge_cell_below = function () {
804 813 var index = this.get_selected_index();
805 814 var cell = this.get_cell(index);
806 815 if (index < this.ncells()-1) {
807 816 lower_cell = this.get_cell(index+1);
808 817 lower_text = lower_cell.get_text();
809 818 text = cell.get_text();
810 819 if (cell instanceof IPython.CodeCell) {
811 820 cell.set_text(text+'\n'+lower_text);
812 821 } else if (cell instanceof IPython.MarkdownCell || cell instanceof IPython.HTMLCell) {
813 822 cell.edit();
814 823 cell.set_text(text+'\n'+lower_text);
815 824 cell.render();
816 825 };
817 826 this.delete_cell(index+1);
818 827 this.select(this.find_cell_index(cell));
819 828 };
820 829 };
821 830
822 831
823 832 // Cell collapsing and output clearing
824 833
825 834 Notebook.prototype.collapse = function (index) {
826 835 var i = this.index_or_selected(index);
827 836 this.get_cell(i).collapse();
828 837 this.dirty = true;
829 838 };
830 839
831 840
832 841 Notebook.prototype.expand = function (index) {
833 842 var i = this.index_or_selected(index);
834 843 this.get_cell(i).expand();
835 844 this.dirty = true;
836 845 };
837 846
838 847
839 848 Notebook.prototype.toggle_output = function (index) {
840 849 var i = this.index_or_selected(index);
841 850 this.get_cell(i).toggle_output();
842 851 this.dirty = true;
843 852 };
844 853
845 854
846 855 Notebook.prototype.set_timebeforetooltip = function (time) {
847 856 this.time_before_tooltip = time;
848 857 };
849 858
850 859
851 860 Notebook.prototype.set_tooltipontab = function (state) {
852 861 this.tooltip_on_tab = state;
853 862 };
854 863
855 864
856 865 Notebook.prototype.set_smartcompleter = function (state) {
857 866 this.smart_completer = state;
858 867 };
859 868
860 869
861 870 Notebook.prototype.clear_all_output = function () {
862 871 var ncells = this.ncells();
863 872 var cells = this.get_cells();
864 873 for (var i=0; i<ncells; i++) {
865 874 if (cells[i] instanceof IPython.CodeCell) {
866 875 cells[i].clear_output(true,true,true);
867 876 }
868 877 };
869 878 this.dirty = true;
870 879 };
871 880
872 881
873 882 // Other cell functions: line numbers, ...
874 883
875 884 Notebook.prototype.cell_toggle_line_numbers = function() {
876 885 this.get_selected_cell().toggle_line_numbers();
877 886 };
878 887
879 888 // Kernel related things
880 889
881 890 Notebook.prototype.start_kernel = function () {
882 891 this.kernel = new IPython.Kernel();
883 var notebook_id = IPython.save_widget.get_notebook_id();
884 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
892 this.kernel.start(this.notebook_id, $.proxy(this.kernel_started, this));
885 893 };
886 894
887 895
888 896 Notebook.prototype.restart_kernel = function () {
889 897 var that = this;
890 var notebook_id = IPython.save_widget.get_notebook_id();
891
892 898 var dialog = $('<div/>');
893 899 dialog.html('Do you want to restart the current kernel? You will lose all variables defined in it.');
894 900 $(document).append(dialog);
895 901 dialog.dialog({
896 902 resizable: false,
897 903 modal: true,
898 904 title: "Restart kernel or continue running?",
899 905 closeText: '',
900 906 buttons : {
901 907 "Restart": function () {
902 908 that.kernel.restart($.proxy(that.kernel_started, that));
903 909 $(this).dialog('close');
904 910 },
905 911 "Continue running": function () {
906 912 $(this).dialog('close');
907 913 }
908 914 }
909 915 });
910 916 };
911 917
912 918
913 919 Notebook.prototype.kernel_started = function () {
914 920 console.log("Kernel started: ", this.kernel.kernel_id);
915 921 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
916 922 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
917 923 };
918 924
919 925
920 926 Notebook.prototype.handle_shell_reply = function (e) {
921 927 reply = $.parseJSON(e.data);
922 928 var header = reply.header;
923 929 var content = reply.content;
924 930 var msg_type = header.msg_type;
925 931 // console.log(reply);
926 932 var cell = this.cell_for_msg(reply.parent_header.msg_id);
927 933 if (msg_type === "execute_reply") {
928 934 cell.set_input_prompt(content.execution_count);
929 935 cell.element.removeClass("running");
930 936 this.dirty = true;
931 937 } else if (msg_type === "complete_reply") {
932 938 cell.finish_completing(content.matched_text, content.matches);
933 939 } else if (msg_type === "object_info_reply"){
934 940 //console.log('back from object_info_request : ')
935 941 rep = reply.content;
936 942 if(rep.found)
937 943 {
938 944 cell.finish_tooltip(rep);
939 945 }
940 946 } else {
941 947 //console.log("unknown reply:"+msg_type);
942 948 }
943 949 // when having a rely from object_info_reply,
944 950 // no payload so no nned to handle it
945 951 if(typeof(content.payload)!='undefined') {
946 952 var payload = content.payload || [];
947 953 this.handle_payload(cell, payload);
948 954 }
949 955 };
950 956
951 957
952 958 Notebook.prototype.handle_payload = function (cell, payload) {
953 959 var l = payload.length;
954 960 for (var i=0; i<l; i++) {
955 961 if (payload[i].source === 'IPython.zmq.page.page') {
956 962 if (payload[i].text.trim() !== '') {
957 963 IPython.pager.clear();
958 964 IPython.pager.expand();
959 965 IPython.pager.append_text(payload[i].text);
960 966 }
961 967 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
962 968 var index = this.find_cell_index(cell);
963 969 var new_cell = this.insert_cell_below('code',index);
964 970 new_cell.set_text(payload[i].text);
965 971 this.dirty = true;
966 972 }
967 973 };
968 974 };
969 975
970 976
971 977 Notebook.prototype.handle_iopub_reply = function (e) {
972 978 reply = $.parseJSON(e.data);
973 979 var content = reply.content;
974 980 // console.log(reply);
975 981 var msg_type = reply.header.msg_type;
976 982 var cell = this.cell_for_msg(reply.parent_header.msg_id);
977 983 if (msg_type !== 'status' && !cell){
978 984 // message not from this notebook, but should be attached to a cell
979 console.log("Received IOPub message not caused by one of my cells");
980 console.log(reply);
985 // console.log("Received IOPub message not caused by one of my cells");
986 // console.log(reply);
981 987 return;
982 988 }
983 989 var output_types = ['stream','display_data','pyout','pyerr'];
984 990 if (output_types.indexOf(msg_type) >= 0) {
985 991 this.handle_output(cell, msg_type, content);
986 992 } else if (msg_type === 'status') {
987 993 if (content.execution_state === 'busy') {
994 $([IPython.events]).trigger('status_busy.Kernel');
988 995 IPython.kernel_status_widget.status_busy();
989 996 } else if (content.execution_state === 'idle') {
990 IPython.kernel_status_widget.status_idle();
997 $([IPython.events]).trigger('status_idle.Kernel');
991 998 } else if (content.execution_state === 'dead') {
992 999 this.handle_status_dead();
993 1000 };
994 1001 } else if (msg_type === 'clear_output') {
995 1002 cell.clear_output(content.stdout, content.stderr, content.other);
996 1003 };
997 1004 };
998 1005
999 1006
1000 1007 Notebook.prototype.handle_status_dead = function () {
1001 1008 var that = this;
1002 1009 this.kernel.stop_channels();
1003 1010 var dialog = $('<div/>');
1004 1011 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
1005 1012 $(document).append(dialog);
1006 1013 dialog.dialog({
1007 1014 resizable: false,
1008 1015 modal: true,
1009 1016 title: "Dead kernel",
1010 1017 buttons : {
1011 1018 "Restart": function () {
1012 1019 that.start_kernel();
1013 1020 $(this).dialog('close');
1014 1021 },
1015 1022 "Continue running": function () {
1016 1023 $(this).dialog('close');
1017 1024 }
1018 1025 }
1019 1026 });
1020 1027 };
1021 1028
1022 1029
1023 1030 Notebook.prototype.handle_output = function (cell, msg_type, content) {
1024 1031 var json = {};
1025 1032 json.output_type = msg_type;
1026 1033 if (msg_type === "stream") {
1027 1034 json.text = content.data;
1028 1035 json.stream = content.name;
1029 1036 } else if (msg_type === "display_data") {
1030 1037 json = this.convert_mime_types(json, content.data);
1031 1038 } else if (msg_type === "pyout") {
1032 1039 json.prompt_number = content.execution_count;
1033 1040 json = this.convert_mime_types(json, content.data);
1034 1041 } else if (msg_type === "pyerr") {
1035 1042 json.ename = content.ename;
1036 1043 json.evalue = content.evalue;
1037 1044 json.traceback = content.traceback;
1038 1045 };
1039 1046 cell.append_output(json);
1040 1047 this.dirty = true;
1041 1048 };
1042 1049
1043 1050
1044 1051 Notebook.prototype.convert_mime_types = function (json, data) {
1045 1052 if (data['text/plain'] !== undefined) {
1046 1053 json.text = data['text/plain'];
1047 1054 };
1048 1055 if (data['text/html'] !== undefined) {
1049 1056 json.html = data['text/html'];
1050 1057 };
1051 1058 if (data['image/svg+xml'] !== undefined) {
1052 1059 json.svg = data['image/svg+xml'];
1053 1060 };
1054 1061 if (data['image/png'] !== undefined) {
1055 1062 json.png = data['image/png'];
1056 1063 };
1057 1064 if (data['image/jpeg'] !== undefined) {
1058 1065 json.jpeg = data['image/jpeg'];
1059 1066 };
1060 1067 if (data['text/latex'] !== undefined) {
1061 1068 json.latex = data['text/latex'];
1062 1069 };
1063 1070 if (data['application/json'] !== undefined) {
1064 1071 json.json = data['application/json'];
1065 1072 };
1066 1073 if (data['application/javascript'] !== undefined) {
1067 1074 json.javascript = data['application/javascript'];
1068 1075 }
1069 1076 return json;
1070 1077 };
1071 1078
1072 1079
1073 1080 Notebook.prototype.execute_selected_cell = function (options) {
1074 1081 // add_new: should a new cell be added if we are at the end of the nb
1075 1082 // terminal: execute in terminal mode, which stays in the current cell
1076 1083 default_options = {terminal: false, add_new: true};
1077 1084 $.extend(default_options, options);
1078 1085 var that = this;
1079 1086 var cell = that.get_selected_cell();
1080 1087 var cell_index = that.find_cell_index(cell);
1081 1088 if (cell instanceof IPython.CodeCell) {
1082 1089 cell.clear_output(true, true, true);
1083 1090 cell.set_input_prompt('*');
1084 1091 cell.element.addClass("running");
1085 1092 var code = cell.get_text();
1086 1093 var msg_id = that.kernel.execute(cell.get_text());
1087 1094 that.msg_cell_map[msg_id] = cell.cell_id;
1088 1095 } else if (cell instanceof IPython.HTMLCell) {
1089 1096 cell.render();
1090 1097 }
1091 1098 if (default_options.terminal) {
1092 1099 cell.select_all();
1093 1100 } else {
1094 1101 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1095 1102 that.insert_cell_below('code');
1096 1103 // If we are adding a new cell at the end, scroll down to show it.
1097 1104 that.scroll_to_bottom();
1098 1105 } else {
1099 1106 that.select(cell_index+1);
1100 1107 };
1101 1108 };
1102 1109 this.dirty = true;
1103 1110 };
1104 1111
1105 1112
1106 1113 Notebook.prototype.execute_all_cells = function () {
1107 1114 var ncells = this.ncells();
1108 1115 for (var i=0; i<ncells; i++) {
1109 1116 this.select(i);
1110 1117 this.execute_selected_cell({add_new:false});
1111 1118 };
1112 1119 this.scroll_to_bottom();
1113 1120 };
1114 1121
1115 1122
1116 1123 Notebook.prototype.request_tool_tip = function (cell,func) {
1117 1124 // Feel free to shorten this logic if you are better
1118 1125 // than me in regEx
1119 1126 // basicaly you shoul be able to get xxx.xxx.xxx from
1120 1127 // something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,
1121 1128 // remove everything between matchin bracket (need to iterate)
1122 1129 matchBracket = /\([^\(\)]+\)/g;
1123 1130 oldfunc = func;
1124 1131 func = func.replace(matchBracket,"");
1125 1132 while( oldfunc != func )
1126 1133 {
1127 1134 oldfunc = func;
1128 1135 func = func.replace(matchBracket,"");
1129 1136 }
1130 1137 // remove everythin after last open bracket
1131 1138 endBracket = /\([^\(]*$/g;
1132 1139 func = func.replace(endBracket,"");
1133 1140 var re = /[a-zA-Z._]+$/g;
1134 1141 var msg_id = this.kernel.object_info_request(re.exec(func));
1135 1142 if(typeof(msg_id)!='undefined'){
1136 1143 this.msg_cell_map[msg_id] = cell.cell_id;
1137 1144 }
1138 1145 };
1139 1146
1140 1147 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
1141 1148 var msg_id = this.kernel.complete(line, cursor_pos);
1142 1149 this.msg_cell_map[msg_id] = cell.cell_id;
1143 1150 };
1144 1151
1152
1145 1153 // Persistance and loading
1146 1154
1155 Notebook.prototype.get_notebook_id = function () {
1156 return this.notebook_id;
1157 };
1158
1159
1160 Notebook.prototype.get_notebook_name = function () {
1161 return this.notebook_name;
1162 };
1163
1164
1165 Notebook.prototype.set_notebook_name = function (name) {
1166 this.notebook_name = name;
1167 };
1168
1169
1170 Notebook.prototype.test_notebook_name = function (nbname) {
1171 nbname = nbname || '';
1172 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
1173 return true;
1174 } else {
1175 return false;
1176 };
1177 };
1178
1147 1179
1148 1180 Notebook.prototype.fromJSON = function (data) {
1149 1181 var ncells = this.ncells();
1150 1182 var i;
1151 1183 for (i=0; i<ncells; i++) {
1152 1184 // Always delete cell 0 as they get renumbered as they are deleted.
1153 1185 this.delete_cell(0);
1154 1186 };
1155 // Save the metadata
1187 // Save the metadata and name.
1156 1188 this.metadata = data.metadata;
1189 this.notebook_name = data.metadata.name;
1157 1190 // Only handle 1 worksheet for now.
1158 1191 var worksheet = data.worksheets[0];
1159 1192 if (worksheet !== undefined) {
1160 1193 var new_cells = worksheet.cells;
1161 1194 ncells = new_cells.length;
1162 1195 var cell_data = null;
1163 1196 var new_cell = null;
1164 1197 for (i=0; i<ncells; i++) {
1165 1198 cell_data = new_cells[i];
1166 1199 new_cell = this.insert_cell_below(cell_data.cell_type);
1167 1200 new_cell.fromJSON(cell_data);
1168 1201 };
1169 1202 };
1170 1203 };
1171 1204
1172 1205
1173 1206 Notebook.prototype.toJSON = function () {
1174 1207 var cells = this.get_cells();
1175 1208 var ncells = cells.length;
1176 1209 cell_array = new Array(ncells);
1177 1210 for (var i=0; i<ncells; i++) {
1178 1211 cell_array[i] = cells[i].toJSON();
1179 1212 };
1180 1213 data = {
1181 1214 // Only handle 1 worksheet for now.
1182 1215 worksheets : [{cells:cell_array}],
1183 1216 metadata : this.metadata
1184 1217 };
1185 1218 return data;
1186 1219 };
1187 1220
1188 1221 Notebook.prototype.save_notebook = function () {
1189 var notebook_id = IPython.save_widget.get_notebook_id();
1190 var nbname = IPython.save_widget.get_notebook_name();
1191 1222 // We may want to move the name/id/nbformat logic inside toJSON?
1192 1223 var data = this.toJSON();
1193 data.metadata.name = nbname;
1224 data.metadata.name = this.notebook_name;
1194 1225 data.nbformat = 3;
1195 1226 // We do the call with settings so we can set cache to false.
1196 1227 var settings = {
1197 1228 processData : false,
1198 1229 cache : false,
1199 1230 type : "PUT",
1200 1231 data : JSON.stringify(data),
1201 1232 headers : {'Content-Type': 'application/json'},
1202 success : $.proxy(this.notebook_saved,this),
1233 success : $.proxy(this.notebook_save_success,this),
1203 1234 error : $.proxy(this.notebook_save_failed,this)
1204 1235 };
1205 IPython.save_widget.status_saving();
1206 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1236 $([IPython.events]).trigger('notebook_saving.Notebook');
1237 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1207 1238 $.ajax(url, settings);
1208 1239 };
1209 1240
1210 1241
1211 Notebook.prototype.notebook_saved = function (data, status, xhr) {
1242 Notebook.prototype.notebook_save_success = function (data, status, xhr) {
1212 1243 this.dirty = false;
1213 IPython.save_widget.notebook_saved();
1214 IPython.save_widget.status_last_saved();
1244 $([IPython.events]).trigger('notebook_saved.Notebook');
1215 1245 };
1216 1246
1217 1247
1218 1248 Notebook.prototype.notebook_save_failed = function (xhr, status, error_msg) {
1219 IPython.save_widget.status_save_failed();
1249 $([IPython.events]).trigger('notebook_save_failed.Notebook');
1220 1250 };
1221 1251
1222 1252
1223 Notebook.prototype.load_notebook = function () {
1253 Notebook.prototype.load_notebook = function (notebook_id) {
1224 1254 var that = this;
1225 var notebook_id = IPython.save_widget.get_notebook_id();
1255 this.notebook_id = notebook_id;
1226 1256 // We do the call with settings so we can set cache to false.
1227 1257 var settings = {
1228 1258 processData : false,
1229 1259 cache : false,
1230 1260 type : "GET",
1231 1261 dataType : "json",
1232 1262 success : function (data, status, xhr) {
1233 that.notebook_loaded(data, status, xhr);
1263 that.load_notebook_success(data, status, xhr);
1234 1264 }
1235 1265 };
1236 IPython.save_widget.status_loading();
1237 var url = $('body').data('baseProjectUrl') + 'notebooks/' + notebook_id;
1266 $([IPython.events]).trigger('notebook_loading.Notebook');
1267 var url = $('body').data('baseProjectUrl') + 'notebooks/' + this.notebook_id;
1238 1268 $.ajax(url, settings);
1239 1269 };
1240 1270
1241 1271
1242 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
1272 Notebook.prototype.load_notebook_success = function (data, status, xhr) {
1243 1273 this.fromJSON(data);
1244 1274 if (this.ncells() === 0) {
1245 1275 this.insert_cell_below('code');
1246 1276 };
1247 IPython.save_widget.status_last_saved();
1248 IPython.save_widget.set_notebook_name(data.metadata.name);
1249 1277 this.dirty = false;
1250 1278 if (! this.read_only) {
1251 1279 this.start_kernel();
1252 1280 }
1253 1281 this.select(0);
1254 1282 this.scroll_to_top();
1255 IPython.save_widget.update_url();
1256 IPython.layout_manager.do_resize();
1283 $([IPython.events]).trigger('notebook_loaded.Notebook');
1257 1284 };
1258 1285
1259 1286 IPython.Notebook = Notebook;
1260 1287
1261 1288
1262 1289 return IPython;
1263 1290
1264 1291 }(IPython));
1265 1292
@@ -1,117 +1,120
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // On document ready
10 10 //============================================================================
11 11
12 12
13 13 $(document).ready(function () {
14 14 if (window.MathJax){
15 15 // MathJax loaded
16 16 MathJax.Hub.Config({
17 17 tex2jax: {
18 18 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
19 19 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
20 20 },
21 21 displayAlign: 'left', // Change this to 'center' to center equations.
22 22 "HTML-CSS": {
23 23 styles: {'.MathJax_Display': {"margin": 0}}
24 24 }
25 25 });
26 26 }else if (window.mathjax_url != ""){
27 27 // Don't have MathJax, but should. Show dialog.
28 28 var dialog = $('<div></div>')
29 29 .append(
30 30 $("<p></p>").addClass('dialog').html(
31 31 "Math/LaTeX rendering will be disabled."
32 32 )
33 33 ).append(
34 34 $("<p></p>").addClass('dialog').html(
35 35 "If you have administrative access to the notebook server and" +
36 36 " a working internet connection, you can install a local copy" +
37 37 " of MathJax for offline use with the following command on the server" +
38 38 " at a Python or IPython prompt:"
39 39 )
40 40 ).append(
41 41 $("<pre></pre>").addClass('dialog').html(
42 42 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
43 43 )
44 44 ).append(
45 45 $("<p></p>").addClass('dialog').html(
46 46 "This will try to install MathJax into the IPython source directory."
47 47 )
48 48 ).append(
49 49 $("<p></p>").addClass('dialog').html(
50 50 "If IPython is installed to a location that requires" +
51 51 " administrative privileges to write, you will need to make this call as" +
52 52 " an administrator, via 'sudo'."
53 53 )
54 54 ).append(
55 55 $("<p></p>").addClass('dialog').html(
56 56 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
57 57 )
58 58 ).append(
59 59 $("<pre></pre>").addClass('dialog').html(
60 60 "$ ipython notebook --no-mathjax"
61 61 )
62 62 ).append(
63 63 $("<p></p>").addClass('dialog').html(
64 64 "which will prevent this dialog from appearing."
65 65 )
66 66 ).dialog({
67 67 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
68 68 width: "70%",
69 69 modal: true,
70 70 })
71 71 }else{
72 72 // No MathJax, but none expected. No dialog.
73 73 }
74 74
75 75
76 76 IPython.markdown_converter = new Markdown.Converter();
77 77 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
78 78
79 79 $('div#header').addClass('border-box-sizing');
80 80 $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
81 81 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
82 82
83 83 IPython.layout_manager = new IPython.LayoutManager();
84 84 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
85 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
86 85 IPython.quick_help = new IPython.QuickHelp('span#quick_help_area');
87 86 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
88 87 IPython.notebook = new IPython.Notebook('div#notebook');
89 IPython.kernel_status_widget = new IPython.KernelStatusWidget('#kernel_status');
88 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
90 89 IPython.menubar = new IPython.MenuBar('#menubar')
91 90 IPython.toolbar = new IPython.ToolBar('#toolbar')
92 IPython.kernel_status_widget.status_idle();
91 IPython.notification_widget = new IPython.NotificationWidget('#notification')
93 92
94 93 IPython.layout_manager.do_resize();
95 94
96 95 // These have display: none in the css file and are made visible here to prevent FLOUC.
97 96 $('div#header').css('display','block');
98 97
99 98 if(IPython.read_only){
100 99 // hide various elements from read-only view
101 100 $('div#pager').remove();
102 101 $('div#pager_splitter').remove();
103 102 $('span#login_widget').removeClass('hidden');
104 103
105 104 // set the notebook name field as not modifiable
106 105 $('#notebook_name').attr('disabled','disabled')
107 106 }
108 107
109 108 $('div#menubar').css('display','block');
110 109 $('div#toolbar').css('display','block');
111 110 $('div#main_app').css('display','block');
112 111
113 112 IPython.layout_manager.do_resize();
114 IPython.notebook.load_notebook();
113 $([IPython.events]).on('notebook_loaded.Notebook', function () {
114 IPython.layout_manager.do_resize();
115 IPython.save_widget.update_url();
116 })
117 IPython.notebook.load_notebook($('body').data('notebookId'));
115 118
116 119 });
117 120
@@ -1,94 +1,93
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // On document ready
10 10 //============================================================================
11 11
12 12
13 13 $(document).ready(function () {
14 14 if (window.MathJax){
15 15 // MathJax loaded
16 16 MathJax.Hub.Config({
17 17 tex2jax: {
18 18 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
19 19 displayMath: [ ['$$','$$'], ["\\[","\\]"] ]
20 20 },
21 21 displayAlign: 'left', // Change this to 'center' to center equations.
22 22 "HTML-CSS": {
23 23 styles: {'.MathJax_Display': {"margin": 0}}
24 24 }
25 25 });
26 26 }else if (window.mathjax_url != ""){
27 27 // Don't have MathJax, but should. Show dialog.
28 28 var dialog = $('<div></div>')
29 29 .append(
30 30 $("<p></p>").addClass('dialog').html(
31 31 "Math/LaTeX rendering will be disabled."
32 32 )
33 33 ).append(
34 34 $("<p></p>").addClass('dialog').html(
35 35 "If you have administrative access to the notebook server and" +
36 36 " a working internet connection, you can install a local copy" +
37 37 " of MathJax for offline use with the following command on the server" +
38 38 " at a Python or IPython prompt:"
39 39 )
40 40 ).append(
41 41 $("<pre></pre>").addClass('dialog').html(
42 42 ">>> from IPython.external import mathjax; mathjax.install_mathjax()"
43 43 )
44 44 ).append(
45 45 $("<p></p>").addClass('dialog').html(
46 46 "This will try to install MathJax into the IPython source directory."
47 47 )
48 48 ).append(
49 49 $("<p></p>").addClass('dialog').html(
50 50 "If IPython is installed to a location that requires" +
51 51 " administrative privileges to write, you will need to make this call as" +
52 52 " an administrator, via 'sudo'."
53 53 )
54 54 ).append(
55 55 $("<p></p>").addClass('dialog').html(
56 56 "When you start the notebook server, you can instruct it to disable MathJax support altogether:"
57 57 )
58 58 ).append(
59 59 $("<pre></pre>").addClass('dialog').html(
60 60 "$ ipython notebook --no-mathjax"
61 61 )
62 62 ).append(
63 63 $("<p></p>").addClass('dialog').html(
64 64 "which will prevent this dialog from appearing."
65 65 )
66 66 ).dialog({
67 67 title: "Failed to retrieve MathJax from '" + window.mathjax_url + "'",
68 68 width: "70%",
69 69 modal: true,
70 70 })
71 71 }else{
72 72 // No MathJax, but none expected. No dialog.
73 73 }
74 74
75 75 IPython.markdown_converter = new Markdown.Converter();
76 76 IPython.read_only = $('meta[name=read_only]').attr("content") == 'True';
77 77
78 78 $('div#header').addClass('border-box-sizing');
79 79 $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
80 80 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
81 81
82 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
83 82 IPython.login_widget = new IPython.LoginWidget('span#login_widget');
84 83 IPython.notebook = new IPython.Notebook('div#notebook');
84 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
85 85
86 86 // These have display: none in the css file and are made visible here to prevent FLOUC.
87 87 $('div#header').css('display','block');
88 88 $('div#main_app').css('display','block');
89 89
90 // Perform these actions after the notebook has been loaded.
91 IPython.notebook.load_notebook(function () {});
90 IPython.notebook.load_notebook($('body').data('notebookId'));
92 91
93 92 });
94 93
@@ -1,179 +1,139
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SaveWidget
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var SaveWidget = function (selector) {
17 17 this.selector = selector;
18 this.notebook_name_blacklist_re = /[\/\\]/;
19 this.last_saved_name = '';
20 18 if (this.selector !== undefined) {
21 19 this.element = $(selector);
22 20 this.style();
23 21 this.bind_events();
24 22 }
25 23 };
26 24
27 25
28 26 SaveWidget.prototype.style = function () {
29 27 this.element.find('span#save_widget').addClass('ui-widget');
30 28 this.element.find('span#notebook_name').addClass('ui-widget ui-widget-content');
31 29 this.element.find('span#save_status').addClass('ui-widget ui-widget-content')
32 30 .css({border: 'none', 'margin-left': '20px'});
33 31 };
34 32
35 33
36 34 SaveWidget.prototype.bind_events = function () {
37 35 var that = this;
38 36 this.element.find('span#notebook_name').click(function () {
39 37 that.rename_notebook();
40 38 });
41 39 this.element.find('span#notebook_name').hover(function () {
42 40 $(this).addClass("ui-state-hover");
43 41 }, function () {
44 42 $(this).removeClass("ui-state-hover");
45 43 });
46 };
47
48
49 SaveWidget.prototype.save_notebook = function () {
50 IPython.notebook.save_notebook();
44 $([IPython.events]).on('notebook_loaded.Notebook', function () {
45 that.set_last_saved();
46 that.update_notebook_name();
47 that.update_document_title();
48 });
49 $([IPython.events]).on('notebook_saved.Notebook', function () {
50 that.set_last_saved();
51 that.update_notebook_name();
52 that.update_document_title();
53 });
54 $([IPython.events]).on('notebook_save_failed.Notebook', function () {
55 that.set_save_status('');
56 });
51 57 };
52 58
53 59
54 60 SaveWidget.prototype.rename_notebook = function () {
55 61 var that = this;
56 62 var dialog = $('<div/>');
57 63 dialog.append(
58 64 $('<h3/>').html('Enter a new notebook name:')
59 65 .css({'margin-bottom': '10px'})
60 66 );
61 67 dialog.append(
62 68 $('<input/>').attr('type','text').attr('size','25')
63 69 .addClass('ui-widget ui-widget-content')
64 .attr('value',that.get_notebook_name())
70 .attr('value',IPython.notebook.get_notebook_name())
65 71 );
66 72 // $(document).append(dialog);
67 73 dialog.dialog({
68 74 resizable: false,
69 75 modal: true,
70 76 title: "Rename Notebook",
71 77 closeText: "",
72 78 close: function(event, ui) {$(this).dialog('destroy').remove();},
73 79 buttons : {
74 80 "OK": function () {
75 81 var new_name = $(this).find('input').attr('value');
76 if (!that.test_notebook_name(new_name)) {
82 if (!IPython.notebook.test_notebook_name(new_name)) {
77 83 $(this).find('h3').html(
78 84 "Invalid notebook name. Notebook names must "+
79 85 "have 1 or more characters and can contain any characters " +
80 86 "except / and \\. Please enter a new notebook name:"
81 87 );
82 88 } else {
83 that.set_notebook_name(new_name);
84 that.save_notebook();
89 IPython.notebook.set_notebook_name(new_name);
90 IPython.notebook.save_notebook();
85 91 $(this).dialog('close');
86 92 }
87 93 },
88 94 "Cancel": function () {
89 95 $(this).dialog('close');
90 96 }
91 97 }
92 98 });
93 99 }
94 100
95 SaveWidget.prototype.notebook_saved = function () {
96 this.set_document_title();
97 this.last_saved_name = this.get_notebook_name();
98 };
99
100 101
101 SaveWidget.prototype.get_notebook_name = function () {
102 return this.element.find('span#notebook_name').html();
103 };
104
105
106 SaveWidget.prototype.set_notebook_name = function (nbname) {
102 SaveWidget.prototype.update_notebook_name = function () {
103 var nbname = IPython.notebook.get_notebook_name();
107 104 this.element.find('span#notebook_name').html(nbname);
108 this.set_document_title();
109 this.last_saved_name = nbname;
110 105 };
111 106
112 107
113 SaveWidget.prototype.set_document_title = function () {
114 nbname = this.get_notebook_name();
108 SaveWidget.prototype.update_document_title = function () {
109 var nbname = IPython.notebook.get_notebook_name();
115 110 document.title = nbname;
116 111 };
117
118
119 SaveWidget.prototype.get_notebook_id = function () {
120 return $('body').data('notebookId');
121 };
122 112
123 113
124 114 SaveWidget.prototype.update_url = function () {
125 var notebook_id = this.get_notebook_id();
126 if (notebook_id !== '') {
115 var notebook_id = IPython.notebook.get_notebook_id();
116 if (notebook_id !== null) {
127 117 var new_url = $('body').data('baseProjectUrl') + notebook_id;
128 118 window.history.replaceState({}, '', new_url);
129 119 };
130 120 };
131 121
132 122
133 SaveWidget.prototype.test_notebook_name = function (nbname) {
134 nbname = nbname || '';
135 if (this.notebook_name_blacklist_re.test(nbname) == false && nbname.length>0) {
136 return true;
137 } else {
138 return false;
139 };
140 };
123 SaveWidget.prototype.set_save_status = function (msg) {
124 this.element.find('span#save_status').html(msg);
125 }
141 126
142 127
143 128 SaveWidget.prototype.set_last_saved = function () {
144 129 var d = new Date();
145 $('#save_status').html('Last saved: '+d.format('mmm dd h:MM TT'));
146
147 };
148
149 SaveWidget.prototype.reset_status = function () {
150 this.element.find('span#save_status').html('');
151 };
152
153
154 SaveWidget.prototype.status_last_saved = function () {
155 this.set_last_saved();
156 };
157
158
159 SaveWidget.prototype.status_saving = function () {
160 this.element.find('span#save_status').html('Saving...');
161 };
162
163
164 SaveWidget.prototype.status_save_failed = function () {
165 this.element.find('span#save_status').html('Save failed');
166 };
167
168
169 SaveWidget.prototype.status_loading = function () {
170 this.element.find('span#save_status').html('Loading...');
130 this.set_save_status('Last saved: '+d.format('mmm dd h:MM TT'));
171 131 };
172 132
173 133
174 134 IPython.SaveWidget = SaveWidget;
175 135
176 136 return IPython;
177 137
178 138 }(IPython));
179 139
@@ -1,146 +1,148
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // ToolBar
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var ToolBar = function (selector) {
15 15 this.selector = selector;
16 16 if (this.selector !== undefined) {
17 17 this.element = $(selector);
18 18 this.style();
19 19 this.bind_events();
20 20 }
21 21 };
22 22
23 23
24 24 ToolBar.prototype.style = function () {
25 25 this.element.addClass('border-box-sizing');
26 26 this.element.find('#cell_type').addClass('ui-widget ui-widget-content');
27 27 this.element.find('#save_b').button({
28 28 icons : {primary: 'ui-icon-disk'},
29 29 text : false
30 30 });
31 31 this.element.find('#cut_b').button({
32 32 icons: {primary: 'ui-icon-scissors'},
33 33 text : false
34 34 });
35 35 this.element.find('#copy_b').button({
36 36 icons: {primary: 'ui-icon-copy'},
37 37 text : false
38 38 });
39 39 this.element.find('#paste_b').button({
40 40 icons: {primary: 'ui-icon-clipboard'},
41 41 text : false
42 42 });
43 43 this.element.find('#cut_copy_paste').buttonset();
44 44 this.element.find('#move_up_b').button({
45 45 icons: {primary: 'ui-icon-arrowthick-1-n'},
46 46 text : false
47 47 });
48 48 this.element.find('#move_down_b').button({
49 49 icons: {primary: 'ui-icon-arrowthick-1-s'},
50 50 text : false
51 51 });
52 52 this.element.find('#move_up_down').buttonset();
53 53 this.element.find('#insert_above_b').button({
54 54 icons: {primary: 'ui-icon-arrowthickstop-1-n'},
55 55 text : false
56 56 });
57 57 this.element.find('#insert_below_b').button({
58 58 icons: {primary: 'ui-icon-arrowthickstop-1-s'},
59 59 text : false
60 60 });
61 61 this.element.find('#insert_above_below').buttonset();
62 62 this.element.find('#run_b').button({
63 63 icons: {primary: 'ui-icon-play'},
64 64 text : false
65 65 });
66 66 this.element.find('#interrupt_b').button({
67 67 icons: {primary: 'ui-icon-stop'},
68 68 text : false
69 69 });
70 70 this.element.find('#run_int').buttonset();
71 71 };
72 72
73 73
74 74 ToolBar.prototype.bind_events = function () {
75 var that = this;
75 76 this.element.find('#save_b').click(function () {
76 IPython.save_widget.save_notebook();
77 IPython.notebook.save_notebook();
77 78 });
78 79 this.element.find('#cut_b').click(function () {
79 80 IPython.notebook.cut_cell();
80 81 });
81 82 this.element.find('#copy_b').click(function () {
82 83 IPython.notebook.copy_cell();
83 84 });
84 85 this.element.find('#paste_b').click(function () {
85 86 IPython.notebook.paste_cell();
86 87 });
87 88 this.element.find('#move_up_b').click(function () {
88 89 IPython.notebook.move_cell_up();
89 90 });
90 91 this.element.find('#move_down_b').click(function () {
91 92 IPython.notebook.move_cell_down();
92 93 });
93 94 this.element.find('#insert_above_b').click(function () {
94 95 IPython.notebook.insert_cell_above('code');
95 96 });
96 97 this.element.find('#insert_below_b').click(function () {
97 98 IPython.notebook.insert_cell_below('code');
98 99 });
99 100 this.element.find('#run_b').click(function () {
100 101 IPython.notebook.execute_selected_cell();
101 102 });
102 103 this.element.find('#interrupt_b').click(function () {
103 104 IPython.notebook.kernel.interrupt();
104 105 });
105 106 this.element.find('#cell_type').change(function () {
106 107 var cell_type = $(this).val();
107 108 if (cell_type === 'code') {
108 109 IPython.notebook.to_code();
109 110 } else if (cell_type === 'markdown') {
110 111 IPython.notebook.to_markdown();
111 112 } else if (cell_type === 'plaintext') {
112 113 IPython.notebook.to_plaintext();
113 114 } else if (cell_type === 'heading1') {
114 115 IPython.notebook.to_heading(undefined, 1);
115 116 } else if (cell_type === 'heading2') {
116 117 IPython.notebook.to_heading(undefined, 2);
117 118 } else if (cell_type === 'heading3') {
118 119 IPython.notebook.to_heading(undefined, 3);
119 120 } else if (cell_type === 'heading4') {
120 121 IPython.notebook.to_heading(undefined, 4);
121 122 } else if (cell_type === 'heading5') {
122 123 IPython.notebook.to_heading(undefined, 5);
123 124 } else if (cell_type === 'heading6') {
124 125 IPython.notebook.to_heading(undefined, 6);
125 126 };
126 127 });
127
128 };
129
130
131 ToolBar.prototype.set_cell_type = function (cell_type) {
132 this.element.find('#cell_type').val(cell_type);
128 $([IPython.events]).on('selected_cell_type_changed', function (event, data) {
129 if (data.cell_type === 'heading') {
130 that.element.find('#cell_type').val(data.cell_type+data.level);
131 } else {
132 that.element.find('#cell_type').val(data.cell_type);
133 }
134 });
133 135 };
134 136
135 137
136 138 ToolBar.prototype.toggle = function () {
137 139 this.element.toggle();
138 140 IPython.layout_manager.do_resize();
139 141 };
140 142
141 143
142 144 IPython.ToolBar = ToolBar;
143 145
144 146 return IPython;
145 147
146 148 }(IPython));
@@ -1,241 +1,244
1 1 <!DOCTYPE HTML>
2 2 <html>
3 3
4 4 <head>
5 5 <meta charset="utf-8">
6 6
7 7 <title>IPython Notebook</title>
8 8
9 9 {% if mathjax_url %}
10 10 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
11 11 {% end %}
12 12 <script type="text/javascript">
13 13 // MathJax disabled, set as null to distingish from *missing* MathJax,
14 14 // where it will be undefined, and should prompt a dialog later.
15 15 window.mathjax_url = "{{mathjax_url}}";
16 16 </script>
17 17
18 18 <link rel="stylesheet" href="{{ static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
19 19 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
20 20 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
21 21
22 22 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
23 23
24 24 <link rel="stylesheet" href="{{ static_url("css/boilerplate.css") }}" type="text/css" />
25 25 <link rel="stylesheet" href="{{ static_url("css/layout.css") }}" type="text/css" />
26 26 <link rel="stylesheet" href="{{ static_url("css/base.css") }}" type="text/css" />
27 27 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
28 28 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
29 29
30 30 {% comment In the notebook, the read-only flag is used to determine %}
31 31 {% comment whether to hide the side panels and switch off input %}
32 32 <meta name="read_only" content="{{read_only and not logged_in}}"/>
33 33
34 34 </head>
35 35
36 36 <body
37 37 data-project={{project}} data-notebook-id={{notebook_id}}
38 38 data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
39 39 >
40 40
41 41 <div id="header">
42 42 <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='{{static_url("ipynblogo.png")}}' alt='IPython Notebook'/></a></h1></span>
43 43 <span id="save_widget">
44 44 <span id="notebook_name"></span>
45 45 <span id="save_status"></span>
46 46 </span>
47 47
48 48 <span id="login_widget">
49 49 {% comment This is a temporary workaround to hide the logout button %}
50 50 {% comment when appropriate until notebook.html is templated %}
51 51 {% if logged_in %}
52 52 <button id="logout">Logout</button>
53 53 {% elif not logged_in and login_available %}
54 54 <button id="login">Login</button>
55 55 {% end %}
56 56 </span>
57
58 <span id="kernel_status">Idle</span>
59 57 </div>
60 58
59 <div id="menubar_container">
61 60 <div id="menubar">
62 61 <ul id="menus">
63 62 <li><a href="#">File</a>
64 63 <ul>
65 64 <li id="new_notebook"><a href="#">New</a></li>
66 65 <li id="open_notebook"><a href="#">Open...</a></li>
67 66 <hr/>
68 67 <li id="copy_notebook"><a href="#">Make a Copy...</a></li>
69 68 <li id="rename_notebook"><a href="#">Rename...</a></li>
70 69 <li id="save_notebook"><a href="#">Save</a></li>
71 70 <hr/>
72 71 <li><a href="#">Download as</a>
73 72 <ul>
74 73 <li id="download_ipynb"><a href="#">IPython (.ipynb)</a></li>
75 74 <li id="download_py"><a href="#">Python (.py)</a></li>
76 75 </ul>
77 76 </li>
78 77 <hr/>
79 78 <li id="print_notebook"><a href="/{{notebook_id}}/print" target="_blank">Print View</a></li>
80 79 </ul>
81 80 </li>
82 81 <li><a href="#">Edit</a>
83 82 <ul>
84 83 <li id="cut_cell"><a href="#">Cut Cell</a></li>
85 84 <li id="copy_cell"><a href="#">Copy Cell</a></li>
86 85 <li id="paste_cell" class="ui-state-disabled"><a href="#">Paste Cell</a></li>
87 86 <li id="paste_cell_above" class="ui-state-disabled"><a href="#">Paste Cell Above</a></li>
88 87 <li id="paste_cell_below" class="ui-state-disabled"><a href="#">Paste Cell Below</a></li>
89 88 <li id="delete_cell"><a href="#">Delete</a></li>
90 89 <hr/>
91 90 <li id="split_cell"><a href="#">Split Cell</a></li>
92 91 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
93 92 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
94 93 <hr/>
95 94 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
96 95 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
97 96 <hr/>
98 97 <li id="select_previous"><a href="#">Select Previous Cell</a></li>
99 98 <li id="select_next"><a href="#">Select Next Cell</a></li>
100 99 </ul>
101 100 </li>
102 101 <li><a href="#">View</a>
103 102 <ul>
104 103 <li id="toggle_header"><a href="#">Toggle Header</a></li>
105 104 <li id="toggle_toolbar"><a href="#">Toggle Toolbar</a></li>
106 105 </ul>
107 106 </li>
108 107 <li><a href="#">Insert</a>
109 108 <ul>
110 109 <li id="insert_cell_above"><a href="#">Insert Cell Above</a></li>
111 110 <li id="insert_cell_below"><a href="#">Insert Cell Below</a></li>
112 111 </ul>
113 112 </li>
114 113 <li><a href="#">Cell</a>
115 114 <ul>
116 115 <li id="run_cell"><a href="#">Run</a></li>
117 116 <li id="run_cell_in_place"><a href="#">Run in Place</a></li>
118 117 <li id="run_all_cells"><a href="#">Run All</a></li>
119 118 <hr/>
120 119 <li id="to_code"><a href="#">Code</a></li>
121 120 <li id="to_markdown"><a href="#">Markdown </a></li>
122 121 <li id="to_plaintext"><a href="#">Plaintext</a></li>
123 122 <li id="to_heading1"><a href="#">Heading 1</a></li>
124 123 <li id="to_heading2"><a href="#">Heading 2</a></li>
125 124 <li id="to_heading3"><a href="#">Heading 3</a></li>
126 125 <li id="to_heading4"><a href="#">Heading 4</a></li>
127 126 <li id="to_heading5"><a href="#">Heading 5</a></li>
128 127 <li id="to_heading6"><a href="#">Heading 6</a></li>
129 128 <hr/>
130 129 <li id="toggle_output"><a href="#">Toggle Output</a></li>
131 130 <li id="clear_all_output"><a href="#">Clear All Output</a></li>
132 131 </ul>
133 132 </li>
134 133 <li><a href="#">Kernel</a>
135 134 <ul>
136 135 <li id="int_kernel"><a href="#">Interrupt</a></li>
137 136 <li id="restart_kernel"><a href="#">Restart</a></li>
138 137 </ul>
139 138 </li>
140 139 <li><a href="#">Help</a>
141 140 <ul>
142 141 <li><a href="http://ipython.org/documentation.html" target="_blank">IPython Help</a></li>
143 142 <li><a href="http://ipython.org/ipython-doc/stable/interactive/htmlnotebook.html" target="_blank">Notebook Help</a></li>
144 143 <li id="keyboard_shortcuts"><a href="#">Keyboard Shortcuts</a></li>
145 144 <hr/>
146 145 <li><a href="http://docs.python.org" target="_blank">Python</a></li>
147 146 <li><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></li>
148 147 <li><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></li>
149 148 <li><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></li>
150 149 <li><a href="http://matplotlib.sourceforge.net/" target="_blank">Matplotlib</a></li>
151 150 </ul>
152 151 </li>
153 152 </ul>
154 153
155 154 </div>
155 <div id="notification"></div>
156 </div>
157
156 158
157 159 <div id="toolbar">
158 160
159 161 <span>
160 162 <button id="save_b">Save</button>
161 163 </span>
162 164 <span id="cut_copy_paste">
163 165 <button id="cut_b" title="Cut Cell">Cut Cell</button>
164 166 <button id="copy_b" title="Copy Cell">Copy Cell</button>
165 167 <button id="paste_b" title="Paste Cell">Paste Cell</button>
166 168 </span>
167 169 <span id="move_up_down">
168 170 <button id="move_up_b" title="Move Cell Up">Move Cell Up</button>
169 171 <button id="move_down_b" title="Move Cell Down">Move Down</button>
170 172 </span>
171 173 <span id="insert_above_below">
172 174 <button id="insert_above_b" title="Insert Cell Above">Insert Cell Above</button>
173 175 <button id="insert_below_b" title="Insert Cell Below">Insert Cell Below</button>
174 176 </span>
175 177 <span id="run_int">
176 178 <button id="run_b" title="Run Cell">Run Cell</button>
177 179 <button id="interrupt_b" title="Interrupt">Interrupt</button>
178 180 </span>
179 181 <span>
180 182 <select id="cell_type">
181 183 <option value="code">Code</option>
182 184 <option value="markdown">Markdown</option>
183 185 <option value="plaintext">Plaintext</option>
184 186 <option value="heading1">Heading 1</option>
185 187 <option value="heading2">Heading 2</option>
186 188 <option value="heading3">Heading 3</option>
187 189 <option value="heading4">Heading 4</option>
188 190 <option value="heading5">Heading 5</option>
189 191 <option value="heading6">Heading 6</option>
190 192 </select>
191 193 </span>
192 194
193 195 </div>
194 196
195 197 <div id="main_app">
196 198
197 199 <div id="notebook_panel">
198 200 <div id="notebook"></div>
199 201 <div id="pager_splitter"></div>
200 202 <div id="pager"></div>
201 203 </div>
202 204
203 205 </div>
204 206
205 207 <script src="{{ static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
206 208 <script src="{{ static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
207 209
208 210 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
209 211 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
210 212 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
211 213 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
212 214 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
213 215 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
214 216 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
215 217 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
216 218
217 219 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
218 220
219 221 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
220 222 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
221 223
222 224 <script src="{{ static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
225 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
223 226 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
224 227 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
225 228 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
226 229 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
227 230 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
228 <script src="{{ static_url("js/kernelstatus.js") }}" type="text/javascript" charset="utf-8"></script>
229 231 <script src="{{ static_url("js/layout.js") }}" type="text/javascript" charset="utf-8"></script>
230 232 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
231 233 <script src="{{ static_url("js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
232 234 <script src="{{ static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
233 235 <script src="{{ static_url("js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
234 236 <script src="{{ static_url("js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
235 237 <script src="{{ static_url("js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
236 238 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
239 <script src="{{ static_url("js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
237 240 <script src="{{ static_url("js/notebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
238 241
239 242 </body>
240 243
241 244 </html>
@@ -1,104 +1,102
1 1 <!DOCTYPE HTML>
2 2 <html>
3 3
4 4 <head>
5 5 <meta charset="utf-8">
6 6
7 7 <title>IPython Notebook</title>
8 8
9 9 {% if mathjax_url %}
10 10 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML" charset="utf-8"></script>
11 11 {% end %}
12 12 <script type="text/javascript">
13 13 // MathJax disabled, set as null to distingish from *missing* MathJax,
14 14 // where it will be undefined, and should prompt a dialog later.
15 15 window.mathjax_url = "{{mathjax_url}}";
16 16 </script>
17 17
18 18 <link rel="stylesheet" href="{{ static_url("jquery/css/themes/base/jquery-ui.min.css") }}" type="text/css" />
19 19 <link rel="stylesheet" href="{{ static_url("codemirror/lib/codemirror.css") }}">
20 <link rel="stylesheet" href="{{ static_url("codemirror/mode/markdown/markdown.css") }}">
21 <link rel="stylesheet" href="{{ static_url("codemirror/mode/rst/rst.css") }}">
22 20 <link rel="stylesheet" href="{{ static_url("codemirror/theme/ipython.css") }}">
23 <link rel="stylesheet" href="{{ static_url("codemirror/theme/default.css") }}">
24 21
25 22 <link rel="stylesheet" href="{{ static_url("prettify/prettify.css") }}"/>
26 23
27 24 <link rel="stylesheet" href="{{ static_url("css/boilerplate.css") }}" type="text/css" />
28 25 <link rel="stylesheet" href="{{ static_url("css/layout.css") }}" type="text/css" />
29 26 <link rel="stylesheet" href="{{ static_url("css/base.css") }}" type="text/css" />
30 27 <link rel="stylesheet" href="{{ static_url("css/notebook.css") }}" type="text/css" />
31 28 <link rel="stylesheet" href="{{ static_url("css/printnotebook.css") }}" type="text/css" />
32 29 <link rel="stylesheet" href="{{ static_url("css/renderedhtml.css") }}" type="text/css" />
33 30
34 31 {% comment In the notebook, the read-only flag is used to determine %}
35 32 {% comment whether to hide the side panels and switch off input %}
36 33 <meta name="read_only" content="{{read_only and not logged_in}}"/>
37 34
38 35 </head>
39 36
40 37 <body
41 38 data-project={{project}} data-notebook-id={{notebook_id}}
42 39 data-base-project-url={{base_project_url}} data-base-kernel-url={{base_kernel_url}}
43 40 >
44 41
45 42 <div id="header">
46 43 <span id="ipython_notebook"><h1><a href='..' alt='dashboard'><img src='{{static_url("ipynblogo.png") }}' alt='IPython Notebook'/></a></h1></span>
47 44 <span id="save_widget">
48 45 <span id="notebook_name"></span>
49 46 <span id="save_status"></span>
50 47 </span>
51 48
52 49 <span id="login_widget">
53 50 {% comment This is a temporary workaround to hide the logout button %}
54 51 {% comment when appropriate until notebook.html is templated %}
55 52 {% if logged_in %}
56 53 <button id="logout">Logout</button>
57 54 {% elif not logged_in and login_available %}
58 55 <button id="login">Login</button>
59 56 {% end %}
60 57 </span>
61 58
62 59 </div>
63 60
64 61
65 62 <div id="main_app">
66 63
67 64 <div id="notebook_panel">
68 65 <div id="notebook"></div>
69 66 </div>
70 67
71 68 </div>
72 69
73 70 <script src="{{ static_url("jquery/js/jquery-1.7.1.min.js") }}" type="text/javascript" charset="utf-8"></script>
74 71 <script src="{{ static_url("jquery/js/jquery-ui.min.js") }}" type="text/javascript" charset="utf-8"></script>
75 72
76 73 <script src="{{ static_url("codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
77 74 <script src="{{ static_url("codemirror/mode/python/python.js") }}" charset="utf-8"></script>
78 75 <script src="{{ static_url("codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
79 76 <script src="{{ static_url("codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
80 77 <script src="{{ static_url("codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
81 78 <script src="{{ static_url("codemirror/mode/css/css.js") }}" charset="utf-8"></script>
82 79 <script src="{{ static_url("codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
83 80 <script src="{{ static_url("codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
84 81
85 82 <script src="{{ static_url("pagedown/Markdown.Converter.js") }}" charset="utf-8"></script>
86 83
87 84 <script src="{{ static_url("prettify/prettify.js") }}" charset="utf-8"></script>
88 85 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
89 86
90 87 <script src="{{ static_url("js/namespace.js") }}" type="text/javascript" charset="utf-8"></script>
88 <script src="{{ static_url("js/events.js") }}" type="text/javascript" charset="utf-8"></script>
91 89 <script src="{{ static_url("js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
92 90 <script src="{{ static_url("js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
93 91 <script src="{{ static_url("js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
94 92 <script src="{{ static_url("js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
95 93 <script src="{{ static_url("js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
96 94 <script src="{{ static_url("js/kernelstatus.js") }}" type="text/javascript" charset="utf-8"></script>
97 95 <script src="{{ static_url("js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
98 96 <script src="{{ static_url("js/loginwidget.js") }}" type="text/javascript" charset="utf-8"></script>
99 97 <script src="{{ static_url("js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
100 98 <script src="{{ static_url("js/printnotebookmain.js") }}" type="text/javascript" charset="utf-8"></script>
101 99
102 100 </body>
103 101
104 102 </html>
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now