##// END OF EJS Templates
Merge pull request #5436 from ivanov/cm-shorcuts-quickhelp...
Min RK -
r16126:1ebcc426 merge
parent child Browse files
Show More
@@ -1,608 +1,566 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 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 // Keyboard management
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14
15 15 var browser = IPython.utils.browser[0];
16 16 var platform = IPython.utils.platform;
17 17
18 18 // Default keyboard shortcuts
19 19
20 20 var default_common_shortcuts = {
21 21 'shift' : {
22 22 help : '',
23 23 help_index : '',
24 24 handler : function (event) {
25 25 // ignore shift keydown
26 26 return true;
27 27 }
28 28 },
29 29 'shift-enter' : {
30 30 help : 'run cell, select below',
31 31 help_index : 'ba',
32 32 handler : function (event) {
33 33 IPython.notebook.execute_cell_and_select_below();
34 34 return false;
35 35 }
36 36 },
37 37 'ctrl-enter' : {
38 38 help : 'run cell',
39 39 help_index : 'bb',
40 40 handler : function (event) {
41 41 IPython.notebook.execute_cell();
42 42 return false;
43 43 }
44 44 },
45 45 'alt-enter' : {
46 46 help : 'run cell, insert below',
47 47 help_index : 'bc',
48 48 handler : function (event) {
49 49 IPython.notebook.execute_cell_and_insert_below();
50 50 return false;
51 51 }
52 52 }
53 53 };
54 54
55 55 if (platform === 'MacOS') {
56 56 default_common_shortcuts['cmd-s'] =
57 57 {
58 58 help : 'save notebook',
59 59 help_index : 'fb',
60 60 handler : function (event) {
61 61 IPython.notebook.save_checkpoint();
62 62 event.preventDefault();
63 63 return false;
64 64 }
65 65 };
66 66 } else {
67 67 default_common_shortcuts['ctrl-s'] =
68 68 {
69 69 help : 'save notebook',
70 70 help_index : 'fb',
71 71 handler : function (event) {
72 72 IPython.notebook.save_checkpoint();
73 73 event.preventDefault();
74 74 return false;
75 75 }
76 76 };
77 77 }
78 78
79 79 // Edit mode defaults
80 80
81 81 var default_edit_shortcuts = {
82 82 'esc' : {
83 83 help : 'command mode',
84 84 help_index : 'aa',
85 85 handler : function (event) {
86 86 IPython.notebook.command_mode();
87 87 return false;
88 88 }
89 89 },
90 90 'ctrl-m' : {
91 91 help : 'command mode',
92 92 help_index : 'ab',
93 93 handler : function (event) {
94 94 IPython.notebook.command_mode();
95 95 return false;
96 96 }
97 97 },
98 98 'up' : {
99 99 help : '',
100 100 help_index : '',
101 101 handler : function (event) {
102 102 var index = IPython.notebook.get_selected_index();
103 103 var cell = IPython.notebook.get_cell(index);
104 104 if (cell && cell.at_top() && index !== 0) {
105 105 event.preventDefault();
106 106 IPython.notebook.command_mode();
107 107 IPython.notebook.select_prev();
108 108 IPython.notebook.edit_mode();
109 109 var cm = IPython.notebook.get_selected_cell().code_mirror;
110 110 cm.setCursor(cm.lastLine(), 0);
111 111 return false;
112 112 } else if (cell) {
113 113 var cm = cell.code_mirror;
114 114 var cursor = cm.getCursor();
115 115 cursor.line -= 1;
116 116 cm.setCursor(cursor);
117 117 return false;
118 118 }
119 119 }
120 120 },
121 121 'down' : {
122 122 help : '',
123 123 help_index : '',
124 124 handler : function (event) {
125 125 var index = IPython.notebook.get_selected_index();
126 126 var cell = IPython.notebook.get_cell(index);
127 127 if (cell.at_bottom() && index !== (IPython.notebook.ncells()-1)) {
128 128 event.preventDefault();
129 129 IPython.notebook.command_mode();
130 130 IPython.notebook.select_next();
131 131 IPython.notebook.edit_mode();
132 132 var cm = IPython.notebook.get_selected_cell().code_mirror;
133 133 cm.setCursor(0, 0);
134 134 return false;
135 135 } else {
136 136 var cm = cell.code_mirror;
137 137 var cursor = cm.getCursor();
138 138 cursor.line += 1;
139 139 cm.setCursor(cursor);
140 140 return false;
141 141 }
142 142 }
143 143 },
144 144 'ctrl-shift--' : {
145 145 help : 'split cell',
146 146 help_index : 'ea',
147 147 handler : function (event) {
148 148 IPython.notebook.split_cell();
149 149 return false;
150 150 }
151 151 },
152 152 'ctrl-shift-subtract' : {
153 153 help : '',
154 154 help_index : 'eb',
155 155 handler : function (event) {
156 156 IPython.notebook.split_cell();
157 157 return false;
158 158 }
159 159 },
160 'tab' : {
161 help : 'indent or complete',
162 help_index : 'ec',
163 },
164 'shift-tab' : {
165 help : 'tooltip',
166 help_index : 'ed',
167 },
168 160 };
169 161
170 if (platform === 'MacOS') {
171 default_edit_shortcuts['cmd-/'] =
172 {
173 help : 'toggle comment',
174 help_index : 'ee'
175 };
176 default_edit_shortcuts['cmd-]'] =
177 {
178 help : 'indent',
179 help_index : 'ef'
180 };
181 default_edit_shortcuts['cmd-['] =
182 {
183 help : 'dedent',
184 help_index : 'eg'
185 };
186 } else {
187 default_edit_shortcuts['ctrl-/'] =
188 {
189 help : 'toggle comment',
190 help_index : 'ee'
191 };
192 default_edit_shortcuts['ctrl-]'] =
193 {
194 help : 'indent',
195 help_index : 'ef'
196 };
197 default_edit_shortcuts['ctrl-['] =
198 {
199 help : 'dedent',
200 help_index : 'eg'
201 };
202 }
203
204 162 // Command mode defaults
205 163
206 164 var default_command_shortcuts = {
207 165 'enter' : {
208 166 help : 'edit mode',
209 167 help_index : 'aa',
210 168 handler : function (event) {
211 169 IPython.notebook.edit_mode();
212 170 return false;
213 171 }
214 172 },
215 173 'up' : {
216 174 help : 'select previous cell',
217 175 help_index : 'da',
218 176 handler : function (event) {
219 177 var index = IPython.notebook.get_selected_index();
220 178 if (index !== 0 && index !== null) {
221 179 IPython.notebook.select_prev();
222 180 IPython.notebook.focus_cell();
223 181 }
224 182 return false;
225 183 }
226 184 },
227 185 'down' : {
228 186 help : 'select next cell',
229 187 help_index : 'db',
230 188 handler : function (event) {
231 189 var index = IPython.notebook.get_selected_index();
232 190 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
233 191 IPython.notebook.select_next();
234 192 IPython.notebook.focus_cell();
235 193 }
236 194 return false;
237 195 }
238 196 },
239 197 'k' : {
240 198 help : 'select previous cell',
241 199 help_index : 'dc',
242 200 handler : function (event) {
243 201 var index = IPython.notebook.get_selected_index();
244 202 if (index !== 0 && index !== null) {
245 203 IPython.notebook.select_prev();
246 204 IPython.notebook.focus_cell();
247 205 }
248 206 return false;
249 207 }
250 208 },
251 209 'j' : {
252 210 help : 'select next cell',
253 211 help_index : 'dd',
254 212 handler : function (event) {
255 213 var index = IPython.notebook.get_selected_index();
256 214 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
257 215 IPython.notebook.select_next();
258 216 IPython.notebook.focus_cell();
259 217 }
260 218 return false;
261 219 }
262 220 },
263 221 'x' : {
264 222 help : 'cut cell',
265 223 help_index : 'ee',
266 224 handler : function (event) {
267 225 IPython.notebook.cut_cell();
268 226 return false;
269 227 }
270 228 },
271 229 'c' : {
272 230 help : 'copy cell',
273 231 help_index : 'ef',
274 232 handler : function (event) {
275 233 IPython.notebook.copy_cell();
276 234 return false;
277 235 }
278 236 },
279 237 'shift-v' : {
280 238 help : 'paste cell above',
281 239 help_index : 'eg',
282 240 handler : function (event) {
283 241 IPython.notebook.paste_cell_above();
284 242 return false;
285 243 }
286 244 },
287 245 'v' : {
288 246 help : 'paste cell below',
289 247 help_index : 'eh',
290 248 handler : function (event) {
291 249 IPython.notebook.paste_cell_below();
292 250 return false;
293 251 }
294 252 },
295 253 'd' : {
296 254 help : 'delete cell (press twice)',
297 255 help_index : 'ej',
298 256 count: 2,
299 257 handler : function (event) {
300 258 IPython.notebook.delete_cell();
301 259 return false;
302 260 }
303 261 },
304 262 'a' : {
305 263 help : 'insert cell above',
306 264 help_index : 'ec',
307 265 handler : function (event) {
308 266 IPython.notebook.insert_cell_above('code');
309 267 IPython.notebook.select_prev();
310 268 IPython.notebook.focus_cell();
311 269 return false;
312 270 }
313 271 },
314 272 'b' : {
315 273 help : 'insert cell below',
316 274 help_index : 'ed',
317 275 handler : function (event) {
318 276 IPython.notebook.insert_cell_below('code');
319 277 IPython.notebook.select_next();
320 278 IPython.notebook.focus_cell();
321 279 return false;
322 280 }
323 281 },
324 282 'y' : {
325 283 help : 'to code',
326 284 help_index : 'ca',
327 285 handler : function (event) {
328 286 IPython.notebook.to_code();
329 287 return false;
330 288 }
331 289 },
332 290 'm' : {
333 291 help : 'to markdown',
334 292 help_index : 'cb',
335 293 handler : function (event) {
336 294 IPython.notebook.to_markdown();
337 295 return false;
338 296 }
339 297 },
340 298 'r' : {
341 299 help : 'to raw',
342 300 help_index : 'cc',
343 301 handler : function (event) {
344 302 IPython.notebook.to_raw();
345 303 return false;
346 304 }
347 305 },
348 306 '1' : {
349 307 help : 'to heading 1',
350 308 help_index : 'cd',
351 309 handler : function (event) {
352 310 IPython.notebook.to_heading(undefined, 1);
353 311 return false;
354 312 }
355 313 },
356 314 '2' : {
357 315 help : 'to heading 2',
358 316 help_index : 'ce',
359 317 handler : function (event) {
360 318 IPython.notebook.to_heading(undefined, 2);
361 319 return false;
362 320 }
363 321 },
364 322 '3' : {
365 323 help : 'to heading 3',
366 324 help_index : 'cf',
367 325 handler : function (event) {
368 326 IPython.notebook.to_heading(undefined, 3);
369 327 return false;
370 328 }
371 329 },
372 330 '4' : {
373 331 help : 'to heading 4',
374 332 help_index : 'cg',
375 333 handler : function (event) {
376 334 IPython.notebook.to_heading(undefined, 4);
377 335 return false;
378 336 }
379 337 },
380 338 '5' : {
381 339 help : 'to heading 5',
382 340 help_index : 'ch',
383 341 handler : function (event) {
384 342 IPython.notebook.to_heading(undefined, 5);
385 343 return false;
386 344 }
387 345 },
388 346 '6' : {
389 347 help : 'to heading 6',
390 348 help_index : 'ci',
391 349 handler : function (event) {
392 350 IPython.notebook.to_heading(undefined, 6);
393 351 return false;
394 352 }
395 353 },
396 354 'o' : {
397 355 help : 'toggle output',
398 356 help_index : 'gb',
399 357 handler : function (event) {
400 358 IPython.notebook.toggle_output();
401 359 return false;
402 360 }
403 361 },
404 362 'shift-o' : {
405 363 help : 'toggle output scrolling',
406 364 help_index : 'gc',
407 365 handler : function (event) {
408 366 IPython.notebook.toggle_output_scroll();
409 367 return false;
410 368 }
411 369 },
412 370 's' : {
413 371 help : 'save notebook',
414 372 help_index : 'fa',
415 373 handler : function (event) {
416 374 IPython.notebook.save_checkpoint();
417 375 return false;
418 376 }
419 377 },
420 378 'ctrl-j' : {
421 379 help : 'move cell down',
422 380 help_index : 'eb',
423 381 handler : function (event) {
424 382 IPython.notebook.move_cell_down();
425 383 return false;
426 384 }
427 385 },
428 386 'ctrl-k' : {
429 387 help : 'move cell up',
430 388 help_index : 'ea',
431 389 handler : function (event) {
432 390 IPython.notebook.move_cell_up();
433 391 return false;
434 392 }
435 393 },
436 394 'l' : {
437 395 help : 'toggle line numbers',
438 396 help_index : 'ga',
439 397 handler : function (event) {
440 398 IPython.notebook.cell_toggle_line_numbers();
441 399 return false;
442 400 }
443 401 },
444 402 'i' : {
445 403 help : 'interrupt kernel (press twice)',
446 404 help_index : 'ha',
447 405 count: 2,
448 406 handler : function (event) {
449 407 IPython.notebook.kernel.interrupt();
450 408 return false;
451 409 }
452 410 },
453 411 '0' : {
454 412 help : 'restart kernel (press twice)',
455 413 help_index : 'hb',
456 414 count: 2,
457 415 handler : function (event) {
458 416 IPython.notebook.restart_kernel();
459 417 return false;
460 418 }
461 419 },
462 420 'h' : {
463 421 help : 'keyboard shortcuts',
464 422 help_index : 'ge',
465 423 handler : function (event) {
466 424 IPython.quick_help.show_keyboard_shortcuts();
467 425 return false;
468 426 }
469 427 },
470 428 'z' : {
471 429 help : 'undo last delete',
472 430 help_index : 'ei',
473 431 handler : function (event) {
474 432 IPython.notebook.undelete_cell();
475 433 return false;
476 434 }
477 435 },
478 436 'shift-m' : {
479 437 help : 'merge cell below',
480 438 help_index : 'ek',
481 439 handler : function (event) {
482 440 IPython.notebook.merge_cell_below();
483 441 return false;
484 442 }
485 443 },
486 444 'q' : {
487 445 help : 'close pager',
488 446 help_index : 'gd',
489 447 handler : function (event) {
490 448 IPython.pager.collapse();
491 449 return false;
492 450 }
493 451 },
494 452 };
495 453
496 454
497 455 // Main keyboard manager for the notebook
498 456
499 457 var ShortcutManager = IPython.keyboard.ShortcutManager;
500 458 var keycodes = IPython.keyboard.keycodes;
501 459
502 460 var KeyboardManager = function () {
503 461 this.mode = 'command';
504 462 this.enabled = true;
505 463 this.bind_events();
506 464 this.command_shortcuts = new ShortcutManager();
507 465 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
508 466 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
509 467 this.edit_shortcuts = new ShortcutManager();
510 468 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
511 469 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
512 470 };
513 471
514 472 KeyboardManager.prototype.bind_events = function () {
515 473 var that = this;
516 474 $(document).keydown(function (event) {
517 475 return that.handle_keydown(event);
518 476 });
519 477 };
520 478
521 479 KeyboardManager.prototype.handle_keydown = function (event) {
522 480 var notebook = IPython.notebook;
523 481
524 482 if (event.which === keycodes.esc) {
525 483 // Intercept escape at highest level to avoid closing
526 484 // websocket connection with firefox
527 485 event.preventDefault();
528 486 }
529 487
530 488 if (!this.enabled) {
531 489 if (event.which === keycodes.esc) {
532 490 // ESC
533 491 notebook.command_mode();
534 492 return false;
535 493 }
536 494 return true;
537 495 }
538 496
539 497 if (this.mode === 'edit') {
540 498 return this.edit_shortcuts.call_handler(event);
541 499 } else if (this.mode === 'command') {
542 500 return this.command_shortcuts.call_handler(event);
543 501 }
544 502 return true;
545 503 };
546 504
547 505 KeyboardManager.prototype.edit_mode = function () {
548 506 this.last_mode = this.mode;
549 507 this.mode = 'edit';
550 508 };
551 509
552 510 KeyboardManager.prototype.command_mode = function () {
553 511 this.last_mode = this.mode;
554 512 this.mode = 'command';
555 513 };
556 514
557 515 KeyboardManager.prototype.enable = function () {
558 516 this.enabled = true;
559 517 };
560 518
561 519 KeyboardManager.prototype.disable = function () {
562 520 this.enabled = false;
563 521 };
564 522
565 523 KeyboardManager.prototype.register_events = function (e) {
566 524 var that = this;
567 525 var handle_focus = function () {
568 526 that.disable();
569 527 };
570 528 var handle_blur = function () {
571 529 that.enable();
572 530 };
573 531 e.on('focusin', handle_focus);
574 532 e.on('focusout', handle_blur);
575 533 // TODO: Very strange. The focusout event does not seem fire for the
576 534 // bootstrap textboxes on FF25&26... This works around that by
577 535 // registering focus and blur events recursively on all inputs within
578 536 // registered element.
579 537 e.find('input').blur(handle_blur);
580 538 e.on('DOMNodeInserted', function (event) {
581 539 var target = $(event.target);
582 540 if (target.is('input')) {
583 541 target.blur(handle_blur);
584 542 } else {
585 543 target.find('input').blur(handle_blur);
586 544 }
587 545 });
588 546 // There are times (raw_input) where we remove the element from the DOM before
589 547 // focusout is called. In this case we bind to the remove event of jQueryUI,
590 548 // which gets triggered upon removal, iff it is focused at the time.
591 549 // is_focused must be used to check for the case where an element within
592 550 // the element being removed is focused.
593 551 e.on('remove', function () {
594 552 if (IPython.utils.is_focused(e[0])) {
595 553 that.enable();
596 554 }
597 555 });
598 556 };
599 557
600 558
601 559 IPython.default_common_shortcuts = default_common_shortcuts;
602 560 IPython.default_edit_shortcuts = default_edit_shortcuts;
603 561 IPython.default_command_shortcuts = default_command_shortcuts;
604 562 IPython.KeyboardManager = KeyboardManager;
605 563
606 564 return IPython;
607 565
608 566 }(IPython));
@@ -1,129 +1,171 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2008-2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
7 3
8 4 //============================================================================
9 5 // QuickHelp button
10 6 //============================================================================
11 7
12 8 var IPython = (function (IPython) {
13 9 "use strict";
14 10
11 var platform = IPython.utils.platform;
12
15 13 var QuickHelp = function (selector) {
16 14 };
17 15
16 var cmd_ctrl = 'Ctrl-';
17 var platform_specific;
18
19 if (platform === 'MacOS') {
20 // Mac OS X specific
21 cmd_ctrl = 'Cmd-';
22 platform_specific = [
23 { shortcut: "Cmd-Up", help:"go to cell start" },
24 { shortcut: "Cmd-Down", help:"go to cell end" },
25 { shortcut: "Opt-Left", help:"go one word left" },
26 { shortcut: "Opt-Right", help:"go one word right" },
27 { shortcut: "Opt-Backspace", help:"del word before" },
28 { shortcut: "Opt-Delete", help:"del word after" },
29 ];
30 } else {
31 // PC specific
32 platform_specific = [
33 { shortcut: "Ctrl-Home", help:"go to cell start" },
34 { shortcut: "Ctrl-Up", help:"go to cell start" },
35 { shortcut: "Ctrl-End", help:"go to cell end" },
36 { shortcut: "Ctrl-Down", help:"go to cell end" },
37 { shortcut: "Ctrl-Left", help:"go one word left" },
38 { shortcut: "Ctrl-Right", help:"go one word right" },
39 { shortcut: "Ctrl-Backspace", help:"del word before" },
40 { shortcut: "Ctrl-Delete", help:"del word after" },
41 ];
42 }
43
44 var cm_shortcuts = [
45 { shortcut:"Tab", help:"code completion or indent" },
46 { shortcut:"Shift-Tab", help:"tooltip" },
47 { shortcut: cmd_ctrl + "]", help:"indent" },
48 { shortcut: cmd_ctrl + "[", help:"dedent" },
49 { shortcut: cmd_ctrl + "a", help:"select all" },
50 { shortcut: cmd_ctrl + "z", help:"undo" },
51 { shortcut: cmd_ctrl + "Shift-z", help:"redo" },
52 { shortcut: cmd_ctrl + "y", help:"redo" },
53 ].concat( platform_specific );
54
55
56
57
58
59
18 60 QuickHelp.prototype.show_keyboard_shortcuts = function () {
19 61 // toggles display of keyboard shortcut dialog
20 62 var that = this;
21 63 if ( this.force_rebuild ) {
22 64 this.shortcut_dialog.remove();
23 65 delete(this.shortcut_dialog);
24 66 this.force_rebuild = false;
25 67 }
26 68 if ( this.shortcut_dialog ){
27 69 // if dialog is already shown, close it
28 70 $(this.shortcut_dialog).modal("toggle");
29 71 return;
30 72 }
31 73 var command_shortcuts = IPython.keyboard_manager.command_shortcuts.help();
32 74 var edit_shortcuts = IPython.keyboard_manager.edit_shortcuts.help();
33 75 var help, shortcut;
34 76 var i, half, n;
35 77 var element = $('<div/>');
36 78
37 79 // The documentation
38 80 var doc = $('<div/>').addClass('alert');
39 81 doc.append(
40 82 $('<button/>').addClass('close').attr('data-dismiss','alert').html('&times;')
41 83 ).append(
42 84 'The IPython Notebook has two different keyboard input modes. <b>Edit mode</b> '+
43 85 'allows you to type code/text into a cell and is indicated by a green cell '+
44 86 'border. <b>Command mode</b> binds the keyboard to notebook level actions '+
45 87 'and is indicated by a grey cell border.'
46 88 );
47 89 element.append(doc);
48 90
49 91 // Command mode
50 92 var cmd_div = this.build_command_help();
51 93 element.append(cmd_div);
52 94
53 95 // Edit mode
54 var edit_div = this.build_edit_help();
96 var edit_div = this.build_edit_help(cm_shortcuts);
55 97 element.append(edit_div);
56 98
57 99 this.shortcut_dialog = IPython.dialog.modal({
58 100 title : "Keyboard shortcuts",
59 101 body : element,
60 102 destroy : false,
61 103 buttons : {
62 104 Close : {}
63 105 }
64 106 });
65 107 this.shortcut_dialog.addClass("modal_stretch");
66 108
67 109 $([IPython.events]).on('rebuild.QuickHelp', function() { that.force_rebuild = true;});
68 110 };
69 111
70 112 QuickHelp.prototype.build_command_help = function () {
71 113 var command_shortcuts = IPython.keyboard_manager.command_shortcuts.help();
72 114 return build_div('<h4>Command Mode (press <code>Esc</code> to enable)</h4>', command_shortcuts);
73 115 };
74 116
75 117 var special_case = { pageup: "PageUp", pagedown: "Page Down", 'minus': '-' };
76 118 var prettify = function (s) {
77 119 s = s.replace(/-$/, 'minus'); // catch shortcuts using '-' key
78 120 var keys = s.split('-');
79 121 var k, i;
80 122 for (i in keys) {
81 123 k = keys[i];
82 124 if ( k.length == 1 ) {
83 125 keys[i] = "<code><strong>" + k + "</strong></code>";
84 126 continue; // leave individual keys lower-cased
85 127 }
86 128 keys[i] = ( special_case[k] ? special_case[k] : k.charAt(0).toUpperCase() + k.slice(1) );
87 129 keys[i] = "<code><strong>" + keys[i] + "</strong></code>";
88 130 }
89 131 return keys.join('-');
90 132
91 133
92 134 };
93 135
94 QuickHelp.prototype.build_edit_help = function () {
136 QuickHelp.prototype.build_edit_help = function (cm_shortcuts) {
95 137 var edit_shortcuts = IPython.keyboard_manager.edit_shortcuts.help();
96 // Edit mode
97 return build_div('<h4>Edit Mode (press <code>Enter</code> to enable)</h4>', edit_shortcuts);
138 jQuery.extend(cm_shortcuts, edit_shortcuts);
139 return build_div('<h4>Edit Mode (press <code>Enter</code> to enable)</h4>', cm_shortcuts);
98 140 };
99 141
100 142 var build_one = function (s) {
101 143 var help = s.help;
102 144 var shortcut = prettify(s.shortcut);
103 145 return $('<div>').addClass('quickhelp').
104 146 append($('<span/>').addClass('shortcut_key').append($(shortcut))).
105 147 append($('<span/>').addClass('shortcut_descr').text(' : ' + help));
106 148
107 149 };
108 150
109 151 var build_div = function (title, shortcuts) {
110 152 var i, half, n;
111 153 var div = $('<div/>').append($(title));
112 154 var sub_div = $('<div/>').addClass('hbox');
113 155 var col1 = $('<div/>').addClass('box-flex1');
114 156 var col2 = $('<div/>').addClass('box-flex1');
115 157 n = shortcuts.length;
116 158 half = ~~(n/2); // Truncate :)
117 159 for (i=0; i<half; i++) { col1.append( build_one(shortcuts[i]) ); }
118 160 for (i=half; i<n; i++) { col2.append( build_one(shortcuts[i]) ); }
119 161 sub_div.append(col1).append(col2);
120 162 div.append(sub_div);
121 163 return div;
122 164 };
123 165
124 166 // Set module variables
125 167 IPython.QuickHelp = QuickHelp;
126 168
127 169 return IPython;
128 170
129 171 }(IPython));
@@ -1,526 +1,522 b''
1 1 .. _htmlnotebook:
2 2
3 3 The IPython Notebook
4 4 ====================
5 5
6 6 Introduction
7 7 ------------
8 8
9 9 The notebook extends the console-based approach to interactive computing in
10 10 a qualitatively new direction, providing a web-based application suitable for
11 11 capturing the whole computation process: developing, documenting, and
12 12 executing code, as well as communicating the results. The IPython notebook
13 13 combines two components:
14 14
15 15 **A web application**: a browser-based tool for interactive authoring of
16 16 documents which combine explanatory text, mathematics, computations and their
17 17 rich media output.
18 18
19 19 **Notebook documents**: a representation of all content visible in the web
20 20 application, including inputs and outputs of the computations, explanatory
21 21 text, mathematics, images, and rich media representations of objects.
22 22
23 23 .. seealso::
24 24
25 25 See the :ref:`installation documentation <installnotebook>` for directions
26 26 on how to install the notebook and its dependencies.
27 27
28 28
29 29 Main features of the web application
30 30 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31 31
32 32 * In-browser editing for code, with automatic syntax highlighting,
33 33 indentation, and tab completion/introspection.
34 34
35 35 * The ability to execute code from the browser, with the results of
36 36 computations attached to the code which generated them.
37 37
38 38 * Displaying the result of computation using rich media representations, such
39 39 as HTML, LaTeX, PNG, SVG, etc. For example, publication-quality figures
40 40 rendered by the matplotlib_ library, can be included inline.
41 41
42 42 * In-browser editing for rich text using the Markdown_ markup language, which
43 43 can provide commentary for the code, is not limited to plain text.
44 44
45 45 * The ability to easily include mathematical notation within markdown cells
46 46 using LaTeX, and rendered natively by MathJax_.
47 47
48 48
49 49
50 50 .. _MathJax: http://www.mathjax.org/
51 51
52 52
53 53 Notebook documents
54 54 ~~~~~~~~~~~~~~~~~~
55 55 Notebook documents contains the inputs and outputs of a interactive session as
56 56 well as additional text that accompanies the code but is not meant for
57 57 execution. In this way, notebook files can serve as a complete computational
58 58 record of a session, interleaving executable code with explanatory text,
59 59 mathematics, and rich representations of resulting objects. These documents
60 60 are internally JSON_ files and are saved with the ``.ipynb`` extension. Since
61 61 JSON is a plain text format, they can be version-controlled and shared with
62 62 colleagues.
63 63
64 64 .. _JSON: http://en.wikipedia.org/wiki/JSON
65 65
66 66 Notebooks may be exported to a range of static formats, including HTML (for
67 67 example, for blog posts), reStructeredText, LaTeX, PDF, and slide shows, via
68 68 the new :ref:`nbconvert <nbconvert>` command.
69 69
70 70 Furthermore, any ``.ipynb`` notebook document available from a public
71 71 URL can be shared via the `IPython Notebook Viewer <nbviewer>`_ (nbviewer_).
72 72 This service loads the notebook document from the URL and renders it as a
73 73 static web page. The results may thus be shared with a colleague, or as a
74 74 public blog post, without other users needing to install IPython themselves.
75 75 In effect, nbviewer_ is simply :ref:`nbconvert <nbconvert>` as a web service,
76 76 so you can do your own static conversions with nbconvert, without relying on
77 77 nbviewer.
78 78
79 79
80 80
81 81 .. seealso::
82 82
83 83 :ref:`Details on the notebook JSON file format <notebook_format>`
84 84
85 85
86 86 Starting the notebook server
87 87 ----------------------------
88 88
89 89 You can start running a notebook server from the command line using the
90 90 following command::
91 91
92 92 ipython notebook
93 93
94 94 This will print some information about the notebook server in your console,
95 95 and open a web browser to the URL of the web application (by default,
96 96 ``http://127.0.0.1:8888``).
97 97
98 98 The landing page of the IPython notebook web application, the **dashboard**,
99 99 shows the notebooks currently available in the notebook directory (by default,
100 100 the directory from which the notebook server was started).
101 101
102 102 You can create new notebooks from the dashboard with the ``New Notebook``
103 103 button, or open existing ones by clicking on their name. You can also drag
104 104 and drop ``.ipynb`` notebooks and standard ``.py`` Python source code files
105 105 into the notebook list area.
106 106
107 107 When starting a notebook server from the command line, you can also open a
108 108 particular notebook directly, bypassing the dashboard, with ``ipython notebook
109 109 my_notebook.ipynb``. The ``.ipynb`` extension is assumed if no extension is
110 110 given.
111 111
112 112 When you are inside an open notebook, the `File | Open...` menu option will
113 113 open the dashboard in a new browser tab, to allow you to open another notebook
114 114 from the notebook directory or to create a new notebook.
115 115
116 116
117 117 .. note::
118 118
119 119 You can start more than one notebook server at the same time, if you want
120 120 to work on notebooks in different directories. By default the first
121 121 notebook server starts on port 8888, and later notebook servers search for
122 122 ports near that one. You can also manually specify the port with the
123 123 ``--port`` option.
124 124
125 125 Creating a new notebook document
126 126 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
127 127
128 128 A new notebook may be created at any time, either from the dashboard, or using
129 129 the `File | New` menu option from within an active notebook. The new notebook
130 130 is created within the same directory and will open in a new browser tab. It
131 131 will also be reflected as a new entry in the notebook list on the dashboard.
132 132
133 133
134 134 Opening notebooks
135 135 ~~~~~~~~~~~~~~~~~
136 136 An open notebook has **exactly one** interactive session connected to an
137 137 :ref:`IPython kernel <ipythonzmq>`, which will execute code sent by the user
138 138 and communicate back results. This kernel remains active if the web browser
139 139 window is closed, and reopening the same notebook from the dashboard will
140 140 reconnect the web application to the same kernel. In the dashboard, notebooks
141 141 with an active kernel have a ``Shutdown`` button next to them, whereas
142 142 notebooks without an active kernel have a ``Delete`` button in its place.
143 143
144 144 Other clients may connect to the same underlying IPython kernel.
145 145 The notebook server always prints to the terminal the full details of
146 146 how to connect to each kernel, with messages such as the following::
147 147
148 148 [NotebookApp] Kernel started: 87f7d2c0-13e3-43df-8bb8-1bd37aaf3373
149 149
150 150 This long string is the kernel's ID which is sufficient for getting the
151 151 information necessary to connect to the kernel. You can also request this
152 152 connection data by running the ``%connect_info`` :ref:`magic
153 153 <magics_explained>`. This will print the same ID information as well as the
154 154 content of the JSON data structure it contains.
155 155
156 156 You can then, for example, manually start a Qt console connected to the *same*
157 157 kernel from the command line, by passing a portion of the ID::
158 158
159 159 $ ipython qtconsole --existing 87f7d2c0
160 160
161 161 Without an ID, ``--existing`` will connect to the most recently
162 162 started kernel. This can also be done by running the ``%qtconsole``
163 163 :ref:`magic <magics_explained>` in the notebook.
164 164
165 165 .. seealso::
166 166
167 167 :ref:`ipythonzmq`
168 168
169 169 Notebook user interface
170 170 -----------------------
171 171
172 172 When you create a new notebook document, you will be presented with the
173 173 **notebook name**, a **menu bar**, a **toolbar** and an empty **code
174 174 cell**.
175 175
176 176 **notebook name**: The name of the notebook document is displayed at the top
177 177 of the page, next to the ``IP[y]: Notebook`` logo. This name reflects the name
178 178 of the ``.ipynb`` notebook document file. Clicking on the notebook name
179 179 brings up a dialog which allows you to rename it. Thus, renaming a notebook
180 180 from "Untitled0" to "My first notebook" in the browser, renames the
181 181 ``Untitled0.ipynb`` file to ``My first notebook.ipynb``.
182 182
183 183 **menu bar**: The menu bar presents different options that may be used to
184 184 manipulate the way the notebook functions.
185 185
186 186 **toolbar**: The tool bar gives a quick way of performing the most-used
187 187 operations within the notebook, by clicking on an icon.
188 188
189 189 **code cell**: the default type of cell, read on for an explanation of cells
190 190
191 191
192 192 Structure of a notebook document
193 193 --------------------------------
194 194
195 195 The notebook consists of a sequence of cells. A cell is a multi-line
196 196 text input field, and its contents can be executed by using
197 197 :kbd:`Shift-Enter`, or by clicking either the "Play" button the toolbar, or
198 198 `Cell | Run` in the menu bar. The execution behavior of a cell is determined
199 199 the cell's type. There are four types of cells: **code cells**, **markdown
200 200 cells**, **raw cells** and **heading cells**. Every cell starts off
201 201 being a **code cell**, but its type can be changed by using a dropdown on the
202 202 toolbar (which will be "Code", initially), or via :ref:`keyboard shortcuts
203 203 <keyboard-shortcuts>`.
204 204
205 205 For more information on the different things you can do in a notebook,
206 206 see the `collection of examples
207 207 <https://github.com/ipython/ipython/tree/master/examples/notebooks#readme>`_.
208 208
209 209 Code cells
210 210 ~~~~~~~~~~
211 211 A *code cell* allows you to edit and write new code, with full syntax
212 212 highlighting and tab completion. By default, the language associated to a code
213 213 cell is Python, but other languages, such as ``Julia`` and ``R``, can be
214 214 handled using :ref:`cell magic commands <magics_explained>`.
215 215
216 216 When a code cell is executed, code that it contains is sent to the kernel
217 217 associated with the notebook. The results that are returned from this
218 218 computation are then displayed in the notebook as the cell's *output*. The
219 219 output is not limited to text, with many other possible forms of output are
220 220 also possible, including ``matplotlib`` figures and HTML tables (as used, for
221 221 example, in the ``pandas`` data analysis package). This is known as IPython's
222 222 *rich display* capability.
223 223
224 224 .. seealso::
225 225
226 226 `Basic Output`_ example notebook
227 227
228 228 `Rich Display System`_ example notebook
229 229
230 230 Markdown cells
231 231 ~~~~~~~~~~~~~~
232 232 You can document the computational process in a literate way, alternating
233 233 descriptive text with code, using *rich text*. In IPython this is accomplished
234 234 by marking up text with the Markdown language. The corresponding cells are
235 235 called *Markdown cells*. The Markdown language provides a simple way to
236 236 perform this text markup, that is, to specify which parts of the text should
237 237 be emphasized (italics), bold, form lists, etc.
238 238
239 239
240 240 When a Markdown cell is executed, the Markdown code is converted into
241 241 the corresponding formatted rich text. Markdown allows arbitrary HTML code for
242 242 formatting.
243 243
244 244 Within Markdown cells, you can also include *mathematics* in a straightforward
245 245 way, using standard LaTeX notation: ``$...$`` for inline mathematics and
246 246 ``$$...$$`` for displayed mathematics. When the Markdown cell is executed,
247 247 the LaTeX portions are automatically rendered in the HTML output as equations
248 248 with high quality typography. This is made possible by MathJax_, which
249 249 supports a `large subset <mathjax_tex>`_ of LaTeX functionality
250 250
251 251 .. _mathjax_tex: http://docs.mathjax.org/en/latest/tex.html
252 252
253 253 Standard mathematics environments defined by LaTeX and AMS-LaTeX (the
254 254 `amsmath` package) also work, such as
255 255 ``\begin{equation}...\end{equation}``, and ``\begin{align}...\end{align}``.
256 256 New LaTeX macros may be defined using standard methods,
257 257 such as ``\newcommand``, by placing them anywhere *between math delimiters* in
258 258 a Markdown cell. These definitions are then available throughout the rest of
259 259 the IPython session.
260 260
261 261 .. seealso::
262 262
263 263 `Markdown Cells`_ example notebook
264 264
265 265 Raw cells
266 266 ~~~~~~~~~
267 267
268 268 *Raw* cells provide a place in which you can write *output* directly.
269 269 Raw cells are not evaluated by the notebook.
270 270 When passed through :ref:`nbconvert <nbconvert>`, raw cells arrive in the
271 271 destination format unmodified. For example, this allows you to type full LaTeX
272 272 into a raw cell, which will only be rendered by LaTeX after conversion by
273 273 nbconvert.
274 274
275 275 Heading cells
276 276 ~~~~~~~~~~~~~
277 277
278 278 You can provide a conceptual structure for your computational document as a
279 279 whole using different levels of headings; there are 6 levels available, from
280 280 level 1 (top level) down to level 6 (paragraph). These can be used later for
281 281 constructing tables of contents, etc. As with Markdown cells, a heading
282 282 cell is replaced by a rich text rendering of the heading when the cell is
283 283 executed.
284 284
285 285
286 286 Basic workflow
287 287 --------------
288 288
289 289 The normal workflow in a notebook is, then, quite similar to a standard
290 290 IPython session, with the difference that you can edit cells in-place multiple
291 291 times until you obtain the desired results, rather than having to
292 292 rerun separate scripts with the ``%run`` magic command.
293 293
294 294
295 295 Typically, you will work on a computational problem in pieces, organizing
296 296 related ideas into cells and moving forward once previous parts work
297 297 correctly. This is much more convenient for interactive exploration than
298 298 breaking up a computation into scripts that must be executed together, as was
299 299 previously necessary, especially if parts of them take a long time to run.
300 300
301 301 At certain moments, it may be necessary to interrupt a calculation which is
302 302 taking too long to complete. This may be done with the `Kernel | Interrupt`
303 303 menu option, or the :kbd:`Ctrl-m i` keyboard shortcut.
304 304 Similarly, it may be necessary or desirable to restart the whole computational
305 305 process, with the `Kernel | Restart` menu option or :kbd:`Ctrl-m .`
306 306 shortcut.
307 307
308 308 A notebook may be downloaded in either a ``.ipynb`` or ``.py`` file from the
309 309 menu option `File | Download as`. Choosing the ``.py`` option downloads a
310 310 Python ``.py`` script, in which all rich output has been removed and the
311 311 content of markdown cells have been inserted as comments.
312 312
313 313 .. seealso::
314 314
315 315 `Running Code in the IPython Notebook`_ example notebook
316 316
317 317 `Basic Output`_ example notebook
318 318
319 319 :ref:`a warning about doing "roundtrip" conversions <note_about_roundtrip>`.
320 320
321 321 .. _keyboard-shortcuts:
322 322
323 323 Keyboard shortcuts
324 324 ~~~~~~~~~~~~~~~~~~
325 325 All actions in the notebook can be performed with the mouse, but keyboard
326 326 shortcuts are also available for the most common ones. The essential shortcuts
327 327 to remember are the following:
328 328
329 329 * :kbd:`Shift-Enter`: run cell
330 330 Execute the current cell, show output (if any), and jump to the next cell
331 331 below. If :kbd:`Shift-Enter` is invoked on the last cell, a new code
332 332 cell will also be created. Note that in the notebook, typing :kbd:`Enter`
333 333 on its own *never* forces execution, but rather just inserts a new line in
334 334 the current cell. :kbd:`Shift-Enter` is equivalent to clicking the
335 335 ``Cell | Run`` menu item.
336 336
337 337 * :kbd:`Ctrl-Enter`: run cell in-place
338 338 Execute the current cell as if it were in "terminal mode", where any
339 339 output is shown, but the cursor *remains* in the current cell. The cell's
340 340 entire contents are selected after execution, so you can just start typing
341 341 and only the new input will be in the cell. This is convenient for doing
342 342 quick experiments in place, or for querying things like filesystem
343 343 content, without needing to create additional cells that you may not want
344 344 to be saved in the notebook.
345 345
346 346 * :kbd:`Alt-Enter`: run cell, insert below
347 347 Executes the current cell, shows the output, and inserts a *new*
348 348 cell between the current cell and the cell below (if one exists). This
349 349 is thus a shortcut for the sequence :kbd:`Shift-Enter`, :kbd:`Ctrl-m a`.
350 350 (:kbd:`Ctrl-m a` adds a new cell above the current one.)
351 351
352 352 * :kbd:`Ctrl-m`:
353 353 This is the prefix for *all* other shortcuts, which consist of :kbd:`Ctrl-m`
354 354 followed by a single letter or character. For example, if you type
355 355 :kbd:`Ctrl-m h` (that is, the sole letter :kbd:`h` after :kbd:`Ctrl-m`),
356 356 IPython will show you all the available keyboard shortcuts.
357 357
358 358
359 359 ..
360 360 TODO: these live in IPython/html/static/notebook/js/quickhelp.js
361 361 They were last updated for IPython 1.0 release, so update them again for
362 362 future releases.
363 363
364 364 Here is the complete set of keyboard shortcuts available:
365 365
366 366 ============ ==========================
367 367 **Shortcut** **Action**
368 368 ------------ --------------------------
369 369 Shift-Enter run cell
370 370 Ctrl-Enter run cell in-place
371 371 Alt-Enter run cell, insert below
372 372 Ctrl-m x cut cell
373 373 Ctrl-m c copy cell
374 374 Ctrl-m v paste cell
375 375 Ctrl-m d delete cell
376 376 Ctrl-m z undo last cell deletion
377 377 Ctrl-m - split cell
378 378 Ctrl-m a insert cell above
379 379 Ctrl-m b insert cell below
380 380 Ctrl-m o toggle output
381 381 Ctrl-m O toggle output scroll
382 382 Ctrl-m l toggle line numbers
383 383 Ctrl-m s save notebook
384 384 Ctrl-m j move cell down
385 385 Ctrl-m k move cell up
386 386 Ctrl-m y code cell
387 387 Ctrl-m m markdown cell
388 388 Ctrl-m t raw cell
389 389 Ctrl-m 1-6 heading 1-6 cell
390 390 Ctrl-m p select previous
391 391 Ctrl-m n select next
392 392 Ctrl-m i interrupt kernel
393 393 Ctrl-m . restart kernel
394 394 Ctrl-m h show keyboard shortcuts
395 395 ============ ==========================
396 396
397 .. seealso::
398
399 :ref:`Some additional Codemirror keyboard shortcuts <cm_keyboard>`
400
401 397
402 398
403 399 Plotting
404 400 --------
405 401 One major feature of the notebook is the ability to display plots that are the
406 402 output of running code cells. IPython is designed to work seamlessly with the
407 403 matplotlib_ plotting library to provide this functionality.
408 404
409 405 To set this up, before any plotting is performed you must execute the
410 406 ``%matplotlib`` :ref:`magic command <magics_explained>`. This performs the
411 407 necessary behind-the-scenes setup for IPython to work correctly hand in hand
412 408 with ``matplotlib``; it does *not*, however, actually execute any Python
413 409 ``import`` commands, that is, no names are added to the namespace.
414 410
415 411 If the ``%matplotlib`` magic is called without an argument, the
416 412 output of a plotting command is displayed using the default ``matplotlib``
417 413 backend in a separate window. Alternatively, the backend can be explicitly
418 414 requested using, for example::
419 415
420 416 %matplotlib gtk
421 417
422 418 A particularly interesting backend, provided by IPython, is the ``inline``
423 419 backend. This is available only for the IPython Notebook and the
424 420 :ref:`IPython QtConsole <qtconsole>`. It can be invoked as follows::
425 421
426 422 %matplotlib inline
427 423
428 424 With this backend, the output of plotting commands is displayed *inline*
429 425 within the notebook, directly below the code cell that produced it. The
430 426 resulting plots will then also be stored in the notebook document.
431 427
432 428 .. seealso::
433 429
434 430 `Plotting with Matplotlib`_ example notebook
435 431
436 432
437 433 Configuring the IPython Notebook
438 434 --------------------------------
439 435 The notebook server can be run with a variety of command line arguments.
440 436 To see a list of available options enter::
441 437
442 438 $ ipython notebook --help
443 439
444 440 Defaults for these options can also be set by creating a file named
445 441 ``ipython_notebook_config.py`` in your IPython *profile folder*. The profile
446 442 folder is a subfolder of your IPython directory; to find out where it is
447 443 located, run::
448 444
449 445 $ ipython locate
450 446
451 447 To create a new set of default configuration files, with lots of information
452 448 on available options, use::
453 449
454 450 $ ipython profile create
455 451
456 452 .. seealso::
457 453
458 454 :ref:`config_overview`, in particular :ref:`Profiles`.
459 455
460 456 :ref:`notebook_security`
461 457
462 458 :ref:`notebook_public_server`
463 459
464 460
465 461 .. _signing_notebooks:
466 462
467 463 Signing Notebooks
468 464 -----------------
469 465
470 466 To prevent untrusted code from executing on users' behalf when notebooks open,
471 467 we have added a signature to the notebook, stored in metadata.
472 468 The notebook server verifies this signature when a notebook is opened.
473 469 If the signature stored in the notebook metadata does not match,
474 470 javascript and HTML output will not be displayed on load,
475 471 and must be regenerated by re-executing the cells.
476 472
477 473 Any notebook that you have executed yourself *in its entirety* will be considered trusted,
478 474 and its HTML and javascript output will be displayed on load.
479 475
480 476 If you need to see HTML or Javascript output without re-executing,
481 477 you can explicitly trust notebooks, such as those shared with you,
482 478 or those that you have written yourself prior to IPython 2.0,
483 479 at the command-line with::
484 480
485 481 $ ipython trust mynotebook.ipynb [other notebooks.ipynb]
486 482
487 483 This just generates a new signature stored in each notebook.
488 484
489 485 You can generate a new notebook signing key with::
490 486
491 487 $ ipython trust --reset
492 488
493 489
494 490 Importing ``.py`` files
495 491 -----------------------
496 492
497 493 ``.py`` files will be imported as a notebook with
498 494 the same basename, but an ``.ipynb`` extension, located in the notebook
499 495 directory. The notebook created will have just one cell, which will contain
500 496 all the code in the ``.py`` file. You can later manually partition this into
501 497 individual cells using the ``Edit | Split Cell`` menu option, or the
502 498 :kbd:`Ctrl-m -` keyboard shortcut.
503 499
504 500 Note that ``.py`` scripts obtained from a notebook document using :doc:`nbconvert <nbconvert>`
505 501 maintain the structure of the notebook in comments. Reimporting such a
506 502 script back into a notebook will preserve this structure.
507 503
508 504 .. _note_about_roundtrip:
509 505
510 506 .. warning::
511 507
512 508 While in simple cases you can "roundtrip" a notebook to Python, edit the
513 509 Python file, and then import it back without loss of main content, this is
514 510 in general *not guaranteed to work*. First, there is extra metadata
515 511 saved in the notebook that may not be saved to the ``.py`` format. And as
516 512 the notebook format evolves in complexity, there will be attributes of the
517 513 notebook that will not survive a roundtrip through the Python form. You
518 514 should think of the Python format as a way to output a script version of a
519 515 notebook and the import capabilities as a way to load existing code to get
520 516 a notebook started. But the Python version is *not* an alternate notebook
521 517 format.
522 518
523 519 .. seealso::
524 520 :ref:`notebook_format`
525 521
526 522 .. include:: ../links.txt
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now