##// END OF EJS Templates
Added platform dep. logic.
Brian E. Granger -
Show More
@@ -1,508 +1,519
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Utilities
10 10 //============================================================================
11 11 IPython.namespace('IPython.utils');
12 12
13 13 IPython.utils = (function (IPython) {
14 14 "use strict";
15 15
16 16 //============================================================================
17 17 // Cross-browser RegEx Split
18 18 //============================================================================
19 19
20 20 // This code has been MODIFIED from the code licensed below to not replace the
21 21 // default browser split. The license is reproduced here.
22 22
23 23 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
24 24 /*!
25 25 * Cross-Browser Split 1.1.1
26 26 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
27 27 * Available under the MIT License
28 28 * ECMAScript compliant, uniform cross-browser split method
29 29 */
30 30
31 31 /**
32 32 * Splits a string into an array of strings using a regex or string
33 33 * separator. Matches of the separator are not included in the result array.
34 34 * However, if `separator` is a regex that contains capturing groups,
35 35 * backreferences are spliced into the result each time `separator` is
36 36 * matched. Fixes browser bugs compared to the native
37 37 * `String.prototype.split` and can be used reliably cross-browser.
38 38 * @param {String} str String to split.
39 39 * @param {RegExp|String} separator Regex or string to use for separating
40 40 * the string.
41 41 * @param {Number} [limit] Maximum number of items to include in the result
42 42 * array.
43 43 * @returns {Array} Array of substrings.
44 44 * @example
45 45 *
46 46 * // Basic use
47 47 * regex_split('a b c d', ' ');
48 48 * // -> ['a', 'b', 'c', 'd']
49 49 *
50 50 * // With limit
51 51 * regex_split('a b c d', ' ', 2);
52 52 * // -> ['a', 'b']
53 53 *
54 54 * // Backreferences in result array
55 55 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
56 56 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
57 57 */
58 58 var regex_split = function (str, separator, limit) {
59 59 // If `separator` is not a regex, use `split`
60 60 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
61 61 return split.call(str, separator, limit);
62 62 }
63 63 var output = [],
64 64 flags = (separator.ignoreCase ? "i" : "") +
65 65 (separator.multiline ? "m" : "") +
66 66 (separator.extended ? "x" : "") + // Proposed for ES6
67 67 (separator.sticky ? "y" : ""), // Firefox 3+
68 68 lastLastIndex = 0,
69 69 // Make `global` and avoid `lastIndex` issues by working with a copy
70 70 separator = new RegExp(separator.source, flags + "g"),
71 71 separator2, match, lastIndex, lastLength;
72 72 str += ""; // Type-convert
73 73
74 74 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
75 75 if (!compliantExecNpcg) {
76 76 // Doesn't need flags gy, but they don't hurt
77 77 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
78 78 }
79 79 /* Values for `limit`, per the spec:
80 80 * If undefined: 4294967295 // Math.pow(2, 32) - 1
81 81 * If 0, Infinity, or NaN: 0
82 82 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
83 83 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
84 84 * If other: Type-convert, then use the above rules
85 85 */
86 86 limit = typeof(limit) === "undefined" ?
87 87 -1 >>> 0 : // Math.pow(2, 32) - 1
88 88 limit >>> 0; // ToUint32(limit)
89 89 while (match = separator.exec(str)) {
90 90 // `separator.lastIndex` is not reliable cross-browser
91 91 lastIndex = match.index + match[0].length;
92 92 if (lastIndex > lastLastIndex) {
93 93 output.push(str.slice(lastLastIndex, match.index));
94 94 // Fix browsers whose `exec` methods don't consistently return `undefined` for
95 95 // nonparticipating capturing groups
96 96 if (!compliantExecNpcg && match.length > 1) {
97 97 match[0].replace(separator2, function () {
98 98 for (var i = 1; i < arguments.length - 2; i++) {
99 99 if (typeof(arguments[i]) === "undefined") {
100 100 match[i] = undefined;
101 101 }
102 102 }
103 103 });
104 104 }
105 105 if (match.length > 1 && match.index < str.length) {
106 106 Array.prototype.push.apply(output, match.slice(1));
107 107 }
108 108 lastLength = match[0].length;
109 109 lastLastIndex = lastIndex;
110 110 if (output.length >= limit) {
111 111 break;
112 112 }
113 113 }
114 114 if (separator.lastIndex === match.index) {
115 115 separator.lastIndex++; // Avoid an infinite loop
116 116 }
117 117 }
118 118 if (lastLastIndex === str.length) {
119 119 if (lastLength || !separator.test("")) {
120 120 output.push("");
121 121 }
122 122 } else {
123 123 output.push(str.slice(lastLastIndex));
124 124 }
125 125 return output.length > limit ? output.slice(0, limit) : output;
126 126 };
127 127
128 128 //============================================================================
129 129 // End contributed Cross-browser RegEx Split
130 130 //============================================================================
131 131
132 132
133 133 var uuid = function () {
134 134 // http://www.ietf.org/rfc/rfc4122.txt
135 135 var s = [];
136 136 var hexDigits = "0123456789ABCDEF";
137 137 for (var i = 0; i < 32; i++) {
138 138 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
139 139 }
140 140 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
141 141 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
142 142
143 143 var uuid = s.join("");
144 144 return uuid;
145 145 };
146 146
147 147
148 148 //Fix raw text to parse correctly in crazy XML
149 149 function xmlencode(string) {
150 150 return string.replace(/\&/g,'&'+'amp;')
151 151 .replace(/</g,'&'+'lt;')
152 152 .replace(/>/g,'&'+'gt;')
153 153 .replace(/\'/g,'&'+'apos;')
154 154 .replace(/\"/g,'&'+'quot;')
155 155 .replace(/`/g,'&'+'#96;');
156 156 }
157 157
158 158
159 159 //Map from terminal commands to CSS classes
160 160 var ansi_colormap = {
161 161 "01":"ansibold",
162 162
163 163 "30":"ansiblack",
164 164 "31":"ansired",
165 165 "32":"ansigreen",
166 166 "33":"ansiyellow",
167 167 "34":"ansiblue",
168 168 "35":"ansipurple",
169 169 "36":"ansicyan",
170 170 "37":"ansigray",
171 171
172 172 "40":"ansibgblack",
173 173 "41":"ansibgred",
174 174 "42":"ansibggreen",
175 175 "43":"ansibgyellow",
176 176 "44":"ansibgblue",
177 177 "45":"ansibgpurple",
178 178 "46":"ansibgcyan",
179 179 "47":"ansibggray"
180 180 };
181 181
182 182 function _process_numbers(attrs, numbers) {
183 183 // process ansi escapes
184 184 var n = numbers.shift();
185 185 if (ansi_colormap[n]) {
186 186 if ( ! attrs["class"] ) {
187 187 attrs["class"] = ansi_colormap[n];
188 188 } else {
189 189 attrs["class"] += " " + ansi_colormap[n];
190 190 }
191 191 } else if (n == "38" || n == "48") {
192 192 // VT100 256 color or 24 bit RGB
193 193 if (numbers.length < 2) {
194 194 console.log("Not enough fields for VT100 color", numbers);
195 195 return;
196 196 }
197 197
198 198 var index_or_rgb = numbers.shift();
199 199 var r,g,b;
200 200 if (index_or_rgb == "5") {
201 201 // 256 color
202 202 var idx = parseInt(numbers.shift());
203 203 if (idx < 16) {
204 204 // indexed ANSI
205 205 // ignore bright / non-bright distinction
206 206 idx = idx % 8;
207 207 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
208 208 if ( ! attrs["class"] ) {
209 209 attrs["class"] = ansiclass;
210 210 } else {
211 211 attrs["class"] += " " + ansiclass;
212 212 }
213 213 return;
214 214 } else if (idx < 232) {
215 215 // 216 color 6x6x6 RGB
216 216 idx = idx - 16;
217 217 b = idx % 6;
218 218 g = Math.floor(idx / 6) % 6;
219 219 r = Math.floor(idx / 36) % 6;
220 220 // convert to rgb
221 221 r = (r * 51);
222 222 g = (g * 51);
223 223 b = (b * 51);
224 224 } else {
225 225 // grayscale
226 226 idx = idx - 231;
227 227 // it's 1-24 and should *not* include black or white,
228 228 // so a 26 point scale
229 229 r = g = b = Math.floor(idx * 256 / 26);
230 230 }
231 231 } else if (index_or_rgb == "2") {
232 232 // Simple 24 bit RGB
233 233 if (numbers.length > 3) {
234 234 console.log("Not enough fields for RGB", numbers);
235 235 return;
236 236 }
237 237 r = numbers.shift();
238 238 g = numbers.shift();
239 239 b = numbers.shift();
240 240 } else {
241 241 console.log("unrecognized control", numbers);
242 242 return;
243 243 }
244 244 if (r !== undefined) {
245 245 // apply the rgb color
246 246 var line;
247 247 if (n == "38") {
248 248 line = "color: ";
249 249 } else {
250 250 line = "background-color: ";
251 251 }
252 252 line = line + "rgb(" + r + "," + g + "," + b + ");"
253 253 if ( !attrs["style"] ) {
254 254 attrs["style"] = line;
255 255 } else {
256 256 attrs["style"] += " " + line;
257 257 }
258 258 }
259 259 }
260 260 }
261 261
262 262 function ansispan(str) {
263 263 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
264 264 // regular ansi escapes (using the table above)
265 265 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
266 266 if (!pattern) {
267 267 // [(01|22|39|)m close spans
268 268 return "</span>";
269 269 }
270 270 // consume sequence of color escapes
271 271 var numbers = pattern.match(/\d+/g);
272 272 var attrs = {};
273 273 while (numbers.length > 0) {
274 274 _process_numbers(attrs, numbers);
275 275 }
276 276
277 277 var span = "<span ";
278 278 for (var attr in attrs) {
279 279 var value = attrs[attr];
280 280 span = span + " " + attr + '="' + attrs[attr] + '"';
281 281 }
282 282 return span + ">";
283 283 });
284 284 };
285 285
286 286 // Transform ANSI color escape codes into HTML <span> tags with css
287 287 // classes listed in the above ansi_colormap object. The actual color used
288 288 // are set in the css file.
289 289 function fixConsole(txt) {
290 290 txt = xmlencode(txt);
291 291 var re = /\033\[([\dA-Fa-f;]*?)m/;
292 292 var opened = false;
293 293 var cmds = [];
294 294 var opener = "";
295 295 var closer = "";
296 296
297 297 // Strip all ANSI codes that are not color related. Matches
298 298 // all ANSI codes that do not end with "m".
299 299 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
300 300 txt = txt.replace(ignored_re, "");
301 301
302 302 // color ansi codes
303 303 txt = ansispan(txt);
304 304 return txt;
305 305 }
306 306
307 307 // Remove chunks that should be overridden by the effect of
308 308 // carriage return characters
309 309 function fixCarriageReturn(txt) {
310 310 var tmp = txt;
311 311 do {
312 312 txt = tmp;
313 313 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
314 314 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
315 315 } while (tmp.length < txt.length);
316 316 return txt;
317 317 }
318 318
319 319 // Locate any URLs and convert them to a anchor tag
320 320 function autoLinkUrls(txt) {
321 321 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
322 322 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
323 323 }
324 324
325 325 // some keycodes that seem to be platform/browser independent
326 326 var keycodes = {
327 327 BACKSPACE: 8,
328 328 TAB : 9,
329 329 ENTER : 13,
330 330 SHIFT : 16,
331 331 CTRL : 17,
332 332 CONTROL : 17,
333 333 ALT : 18,
334 334 CAPS_LOCK: 20,
335 335 ESC : 27,
336 336 SPACE : 32,
337 337 PGUP : 33,
338 338 PGDOWN : 34,
339 339 END : 35,
340 340 HOME : 36,
341 341 LEFT_ARROW: 37,
342 342 LEFTARROW: 37,
343 343 LEFT : 37,
344 344 UP_ARROW : 38,
345 345 UPARROW : 38,
346 346 UP : 38,
347 347 RIGHT_ARROW:39,
348 348 RIGHTARROW:39,
349 349 RIGHT : 39,
350 350 DOWN_ARROW: 40,
351 351 DOWNARROW: 40,
352 352 DOWN : 40,
353 353 I : 73,
354 354 M : 77,
355 355 // all three of these keys may be COMMAND on OS X:
356 356 LEFT_SUPER : 91,
357 357 RIGHT_SUPER : 92,
358 358 COMMAND : 93,
359 359 };
360 360
361 361 // trigger a key press event
362 362 var press = function (key) {
363 363 var key_press = $.Event('keydown', {which: key});
364 364 $(document).trigger(key_press);
365 365 }
366 366
367 367 var press_up = function() { press(keycodes.UP); };
368 368 var press_down = function() { press(keycodes.DOWN); };
369 369
370 370 var press_ctrl_enter = function() {
371 371 $(document).trigger($.Event('keydown', {which: keycodes.ENTER, ctrlKey: true}));
372 372 };
373 373
374 374 var press_shift_enter = function() {
375 375 $(document).trigger($.Event('keydown', {which: keycodes.ENTER, shiftKey: true}));
376 376 };
377 377
378 378 // trigger the ctrl-m shortcut followed by one of our keys
379 379 var press_ghetto = function(key) {
380 380 $(document).trigger($.Event('keydown', {which: keycodes.M, ctrlKey: true}));
381 381 press(key);
382 382 };
383 383
384 384
385 385 var points_to_pixels = function (points) {
386 386 // A reasonably good way of converting between points and pixels.
387 387 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
388 388 $(body).append(test);
389 389 var pixel_per_point = test.width()/10000;
390 390 test.remove();
391 391 return Math.floor(points*pixel_per_point);
392 392 };
393 393
394 394 var always_new = function (constructor) {
395 395 // wrapper around contructor to avoid requiring `var a = new constructor()`
396 396 // useful for passing constructors as callbacks,
397 397 // not for programmer laziness.
398 398 // from http://programmers.stackexchange.com/questions/118798
399 399 return function () {
400 400 var obj = Object.create(constructor.prototype);
401 401 constructor.apply(obj, arguments);
402 402 return obj;
403 403 };
404 404 };
405 405
406 406
407 407 var url_path_join = function () {
408 408 // join a sequence of url components with '/'
409 409 var url = '';
410 410 for (var i = 0; i < arguments.length; i++) {
411 411 if (arguments[i] === '') {
412 412 continue;
413 413 }
414 414 if (url.length > 0 && url[url.length-1] != '/') {
415 415 url = url + '/' + arguments[i];
416 416 } else {
417 417 url = url + arguments[i];
418 418 }
419 419 }
420 420 return url;
421 421 };
422 422
423 423
424 424 var encode_uri_components = function (uri) {
425 425 // encode just the components of a multi-segment uri,
426 426 // leaving '/' separators
427 427 return uri.split('/').map(encodeURIComponent).join('/');
428 428 }
429 429
430 430 var url_join_encode = function () {
431 431 // join a sequence of url components with '/',
432 432 // encoding each component with encodeURIComponent
433 433 return encode_uri_components(url_path_join.apply(null, arguments));
434 434 };
435 435
436 436
437 437 var splitext = function (filename) {
438 438 // mimic Python os.path.splitext
439 439 // Returns ['base', '.ext']
440 440 var idx = filename.lastIndexOf('.');
441 441 if (idx > 0) {
442 442 return [filename.slice(0, idx), filename.slice(idx)];
443 443 } else {
444 444 return [filename, ''];
445 445 }
446 446 }
447 447
448 448
449 449 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
450 450 var browser = (function() {
451 451 if (typeof navigator === 'undefined') {
452 452 // navigator undefined in node
453 453 return 'None';
454 454 }
455 455 var N= navigator.appName, ua= navigator.userAgent, tem;
456 456 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
457 457 if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1];
458 458 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
459 459 return M;
460 460 })();
461 461
462 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
463 var platform = (function () {
464 var OSName="Unknown OS";
465 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
466 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
467 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
468 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
469 return OSName
470 })();
471
462 472 var is_or_has = function (a, b) {
463 473 // Is b a child of a or a itself?
464 474 return a.has(b).length !==0 || a.is(b);
465 475 }
466 476
467 477 var is_focused = function (e) {
468 478 // Is element e, or one of its children focused?
469 479 e = $(e);
470 480 var target = $(document.activeElement);
471 481 if (target.length > 0) {
472 482 if (is_or_has(e, target)) {
473 483 return true;
474 484 } else {
475 485 return false;
476 486 }
477 487 } else {
478 488 return false;
479 489 }
480 490 }
481 491
482 492
483 493 return {
484 494 regex_split : regex_split,
485 495 uuid : uuid,
486 496 fixConsole : fixConsole,
487 497 keycodes : keycodes,
488 498 press : press,
489 499 press_up : press_up,
490 500 press_down : press_down,
491 501 press_ctrl_enter : press_ctrl_enter,
492 502 press_shift_enter : press_shift_enter,
493 503 press_ghetto : press_ghetto,
494 504 fixCarriageReturn : fixCarriageReturn,
495 505 autoLinkUrls : autoLinkUrls,
496 506 points_to_pixels : points_to_pixels,
497 507 url_path_join : url_path_join,
498 508 url_join_encode : url_join_encode,
499 509 encode_uri_components : encode_uri_components,
500 510 splitext : splitext,
501 511 always_new : always_new,
502 512 browser : browser,
513 platform: platform,
503 514 is_or_has : is_or_has,
504 515 is_focused : is_focused
505 516 };
506 517
507 518 }(IPython));
508 519
@@ -1,709 +1,770
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 // Setup global keycodes and inverse keycodes.
16 16
17 17 // See http://unixpapa.com/js/key.html for a complete description. The short of
18 18 // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
19 19 // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
20 20 // but have minor differences.
21 21
22 22 // These apply to Firefox, (Webkit and IE)
23 23 var _keycodes = {
24 24 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
25 25 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
26 26 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
27 27 '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
28 28 '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
29 29 '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
30 30 '\\ |': 220, '\' "': 222,
31 31 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
32 32 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
33 33 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
34 34 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
35 35 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
36 36 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
37 37 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
38 38 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
39 39 'insert': 45, 'delete': 46, 'numlock': 144,
40 40 };
41 41
42 42 // These apply to Firefox and Opera
43 43 var _mozilla_keycodes = {
44 44 '; :': 59, '= +': 61, '- _': 173, 'meta': 224
45 45 }
46 46
47 47 // This apply to Webkit and IE
48 48 var _ie_keycodes = {
49 49 '; :': 186, '= +': 187, '- _': 189,
50 50 }
51 51
52 52 var browser = IPython.utils.browser[0];
53 var platform = IPython.utils.platform;
53 54
54 55 if (browser === 'Firefox' || browser === 'Opera') {
55 56 $.extend(_keycodes, _mozilla_keycodes);
56 57 } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
57 58 $.extend(_keycodes, _ie_keycodes);
58 59 }
59 60
60 61 var keycodes = {};
61 62 var inv_keycodes = {};
62 63 for (var name in _keycodes) {
63 64 var names = name.split(' ');
64 65 if (names.length === 1) {
65 66 var n = names[0]
66 67 keycodes[n] = _keycodes[n]
67 68 inv_keycodes[_keycodes[n]] = n
68 69 } else {
69 70 var primary = names[0];
70 71 var secondary = names[1];
71 72 keycodes[primary] = _keycodes[name]
72 73 keycodes[secondary] = _keycodes[name]
73 74 inv_keycodes[_keycodes[name]] = primary
74 75 }
75 76 }
76 77
77 78
78 79 // Default keyboard shortcuts
79 80
80 81 var default_common_shortcuts = {
81 'meta+s' : {
82 help : 'save notebook',
83 help_index : 'fb',
84 handler : function (event) {
85 IPython.notebook.save_checkpoint();
86 event.preventDefault();
87 return false;
88 }
89 },
90 'ctrl+s' : {
91 help : 'save notebook',
92 help_index : 'fc',
93 handler : function (event) {
94 IPython.notebook.save_checkpoint();
95 event.preventDefault();
96 return false;
97 }
98 },
99 82 'shift' : {
100 83 help : '',
101 84 help_index : '',
102 85 handler : function (event) {
103 86 // ignore shift keydown
104 87 return true;
105 88 }
106 89 },
107 90 'shift+enter' : {
108 91 help : 'run cell',
109 92 help_index : 'ba',
110 93 handler : function (event) {
111 94 IPython.notebook.execute_cell();
112 95 return false;
113 96 }
114 97 },
115 98 'ctrl+enter' : {
116 99 help : 'run cell, select below',
117 100 help_index : 'bb',
118 101 handler : function (event) {
119 102 IPython.notebook.execute_cell_and_select_below();
120 103 return false;
121 104 }
122 105 },
123 106 'alt+enter' : {
124 107 help : 'run cell, insert below',
125 108 help_index : 'bc',
126 109 handler : function (event) {
127 110 IPython.notebook.execute_cell_and_insert_below();
128 111 return false;
129 112 }
130 113 }
131 114 }
132 115
116 if (platform === 'MacOS') {
117 default_common_shortcuts['cmd+s'] =
118 {
119 help : 'save notebook',
120 help_index : 'fb',
121 handler : function (event) {
122 IPython.notebook.save_checkpoint();
123 event.preventDefault();
124 return false;
125 }
126 };
127 } else {
128 default_common_shortcuts['ctrl+s'] =
129 {
130 help : 'save notebook',
131 help_index : 'fb',
132 handler : function (event) {
133 IPython.notebook.save_checkpoint();
134 event.preventDefault();
135 return false;
136 }
137 };
138 }
139
133 140 // Edit mode defaults
134 141
135 142 var default_edit_shortcuts = {
136 143 'esc' : {
137 144 help : 'command mode',
138 145 help_index : 'aa',
139 146 handler : function (event) {
140 147 IPython.notebook.command_mode();
141 148 IPython.notebook.focus_cell();
142 149 return false;
143 150 }
144 151 },
145 152 'ctrl+m' : {
146 153 help : 'command mode',
147 154 help_index : 'ab',
148 155 handler : function (event) {
149 156 IPython.notebook.command_mode();
150 157 IPython.notebook.focus_cell();
151 158 return false;
152 159 }
153 160 },
154 161 'up' : {
155 162 help : '',
156 163 help_index : '',
157 164 handler : function (event) {
158 165 var cell = IPython.notebook.get_selected_cell();
159 166 if (cell && cell.at_top()) {
160 167 event.preventDefault();
161 168 IPython.notebook.command_mode()
162 169 IPython.notebook.select_prev();
163 170 IPython.notebook.edit_mode();
164 171 return false;
165 172 };
166 173 }
167 174 },
168 175 'down' : {
169 176 help : '',
170 177 help_index : '',
171 178 handler : function (event) {
172 179 var cell = IPython.notebook.get_selected_cell();
173 180 if (cell && cell.at_bottom()) {
174 181 event.preventDefault();
175 182 IPython.notebook.command_mode()
176 183 IPython.notebook.select_next();
177 184 IPython.notebook.edit_mode();
178 185 return false;
179 186 };
180 187 }
181 188 },
182 189 'alt+-' : {
183 190 help : 'split cell',
184 191 help_index : 'ea',
185 192 handler : function (event) {
186 193 IPython.notebook.split_cell();
187 194 return false;
188 195 }
189 196 },
190 197 'alt+subtract' : {
191 198 help : '',
192 199 help_index : 'eb',
193 200 handler : function (event) {
194 201 IPython.notebook.split_cell();
195 202 return false;
196 203 }
197 204 },
205 'tab' : {
206 help : 'indent or complete',
207 help_index : 'ec',
208 },
209 'shift+tab' : {
210 help : 'tooltip',
211 help_index : 'ed',
212 },
213 }
214
215 if (platform === 'MacOS') {
216 default_edit_shortcuts['cmd+/'] =
217 {
218 help : 'toggle comment',
219 help_index : 'ee'
220 };
221 default_edit_shortcuts['cmd+]'] =
222 {
223 help : 'indent',
224 help_index : 'ef'
225 };
226 default_edit_shortcuts['cmd+['] =
227 {
228 help : 'dedent',
229 help_index : 'eg'
230 };
231 } else {
232 default_edit_shortcuts['ctrl+/'] =
233 {
234 help : 'toggle comment',
235 help_index : 'ee'
236 };
237 default_edit_shortcuts['ctrl+]'] =
238 {
239 help : 'indent',
240 help_index : 'ef'
241 };
242 default_edit_shortcuts['ctrl+['] =
243 {
244 help : 'dedent',
245 help_index : 'eg'
246 };
198 247 }
199 248
200 249 // Command mode defaults
201 250
202 251 var default_command_shortcuts = {
203 252 'enter' : {
204 253 help : 'edit mode',
205 254 help_index : 'aa',
206 255 handler : function (event) {
207 256 IPython.notebook.edit_mode();
208 257 return false;
209 258 }
210 259 },
211 260 'up' : {
212 261 help : 'select previous cell',
213 262 help_index : 'da',
214 263 handler : function (event) {
215 264 var index = IPython.notebook.get_selected_index();
216 265 if (index !== 0 && index !== null) {
217 266 IPython.notebook.select_prev();
218 267 var cell = IPython.notebook.get_selected_cell();
219 268 cell.focus_cell();
220 269 };
221 270 return false;
222 271 }
223 272 },
224 273 'down' : {
225 274 help : 'select next cell',
226 275 help_index : 'db',
227 276 handler : function (event) {
228 277 var index = IPython.notebook.get_selected_index();
229 278 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
230 279 IPython.notebook.select_next();
231 280 var cell = IPython.notebook.get_selected_cell();
232 281 cell.focus_cell();
233 282 };
234 283 return false;
235 284 }
236 285 },
237 286 'k' : {
238 287 help : 'select previous cell',
239 288 help_index : 'dc',
240 289 handler : function (event) {
241 290 var index = IPython.notebook.get_selected_index();
242 291 if (index !== 0 && index !== null) {
243 292 IPython.notebook.select_prev();
244 293 var cell = IPython.notebook.get_selected_cell();
245 294 cell.focus_cell();
246 295 };
247 296 return false;
248 297 }
249 298 },
250 299 'j' : {
251 300 help : 'select next cell',
252 301 help_index : 'dd',
253 302 handler : function (event) {
254 303 var index = IPython.notebook.get_selected_index();
255 304 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
256 305 IPython.notebook.select_next();
257 306 var cell = IPython.notebook.get_selected_cell();
258 307 cell.focus_cell();
259 308 };
260 309 return false;
261 310 }
262 311 },
263 312 'x' : {
264 313 help : 'cut cell',
265 314 help_index : 'ee',
266 315 handler : function (event) {
267 316 IPython.notebook.cut_cell();
268 317 return false;
269 318 }
270 319 },
271 320 'c' : {
272 321 help : 'copy cell',
273 322 help_index : 'ef',
274 323 handler : function (event) {
275 324 IPython.notebook.copy_cell();
276 325 return false;
277 326 }
278 327 },
279 328 'shift+v' : {
280 329 help : 'paste cell above',
281 330 help_index : 'eg',
282 331 handler : function (event) {
283 332 IPython.notebook.paste_cell_above();
284 333 return false;
285 334 }
286 335 },
287 336 'v' : {
288 337 help : 'paste cell below',
289 338 help_index : 'eh',
290 339 handler : function (event) {
291 340 IPython.notebook.paste_cell_below();
292 341 return false;
293 342 }
294 343 },
295 344 'd' : {
296 345 help : 'delete cell (press twice)',
297 346 help_index : 'ej',
298 347 count: 2,
299 348 handler : function (event) {
300 349 IPython.notebook.delete_cell();
301 350 return false;
302 351 }
303 352 },
304 353 'a' : {
305 354 help : 'insert cell above',
306 355 help_index : 'ec',
307 356 handler : function (event) {
308 357 IPython.notebook.insert_cell_above('code');
309 358 IPython.notebook.select_prev();
310 359 IPython.notebook.focus_cell();
311 360 return false;
312 361 }
313 362 },
314 363 'b' : {
315 364 help : 'insert cell below',
316 365 help_index : 'ed',
317 366 handler : function (event) {
318 367 IPython.notebook.insert_cell_below('code');
319 368 IPython.notebook.select_next();
320 369 IPython.notebook.focus_cell();
321 370 return false;
322 371 }
323 372 },
324 373 'y' : {
325 374 help : 'to code',
326 375 help_index : 'ca',
327 376 handler : function (event) {
328 377 IPython.notebook.to_code();
329 378 return false;
330 379 }
331 380 },
332 381 'm' : {
333 382 help : 'to markdown',
334 383 help_index : 'cb',
335 384 handler : function (event) {
336 385 IPython.notebook.to_markdown();
337 386 return false;
338 387 }
339 388 },
340 389 'r' : {
341 390 help : 'to raw',
342 391 help_index : 'cc',
343 392 handler : function (event) {
344 393 IPython.notebook.to_raw();
345 394 return false;
346 395 }
347 396 },
348 397 '1' : {
349 398 help : 'to heading 1',
350 399 help_index : 'cd',
351 400 handler : function (event) {
352 401 IPython.notebook.to_heading(undefined, 1);
353 402 return false;
354 403 }
355 404 },
356 405 '2' : {
357 406 help : 'to heading 2',
358 407 help_index : 'ce',
359 408 handler : function (event) {
360 409 IPython.notebook.to_heading(undefined, 2);
361 410 return false;
362 411 }
363 412 },
364 413 '3' : {
365 414 help : 'to heading 3',
366 415 help_index : 'cf',
367 416 handler : function (event) {
368 417 IPython.notebook.to_heading(undefined, 3);
369 418 return false;
370 419 }
371 420 },
372 421 '4' : {
373 422 help : 'to heading 4',
374 423 help_index : 'cg',
375 424 handler : function (event) {
376 425 IPython.notebook.to_heading(undefined, 4);
377 426 return false;
378 427 }
379 428 },
380 429 '5' : {
381 430 help : 'to heading 5',
382 431 help_index : 'ch',
383 432 handler : function (event) {
384 433 IPython.notebook.to_heading(undefined, 5);
385 434 return false;
386 435 }
387 436 },
388 437 '6' : {
389 438 help : 'to heading 6',
390 439 help_index : 'ci',
391 440 handler : function (event) {
392 441 IPython.notebook.to_heading(undefined, 6);
393 442 return false;
394 443 }
395 444 },
396 445 'o' : {
397 446 help : 'toggle output',
398 447 help_index : 'gb',
399 448 handler : function (event) {
400 449 IPython.notebook.toggle_output();
401 450 return false;
402 451 }
403 452 },
404 453 'shift+o' : {
405 help : 'toggle output',
454 help : 'toggle output scroll',
406 455 help_index : 'gc',
407 456 handler : function (event) {
408 457 IPython.notebook.toggle_output_scroll();
409 458 return false;
410 459 }
411 460 },
412 461 's' : {
413 462 help : 'save notebook',
414 463 help_index : 'fa',
415 464 handler : function (event) {
416 465 IPython.notebook.save_checkpoint();
417 466 return false;
418 467 }
419 468 },
420 469 'ctrl+j' : {
421 470 help : 'move cell down',
422 471 help_index : 'eb',
423 472 handler : function (event) {
424 473 IPython.notebook.move_cell_down();
425 474 return false;
426 475 }
427 476 },
428 477 'ctrl+k' : {
429 478 help : 'move cell up',
430 479 help_index : 'ea',
431 480 handler : function (event) {
432 481 IPython.notebook.move_cell_up();
433 482 return false;
434 483 }
435 484 },
436 485 'l' : {
437 486 help : 'toggle line numbers',
438 487 help_index : 'ga',
439 488 handler : function (event) {
440 489 IPython.notebook.cell_toggle_line_numbers();
441 490 return false;
442 491 }
443 492 },
444 493 'i' : {
445 494 help : 'interrupt kernel (press twice)',
446 495 help_index : 'ha',
447 496 count: 2,
448 497 handler : function (event) {
449 498 IPython.notebook.kernel.interrupt();
450 499 return false;
451 500 }
452 501 },
453 502 '0' : {
454 503 help : 'restart kernel (press twice)',
455 504 help_index : 'hb',
456 505 count: 2,
457 506 handler : function (event) {
458 507 IPython.notebook.restart_kernel();
459 508 return false;
460 509 }
461 510 },
462 511 'h' : {
463 512 help : 'keyboard shortcuts',
464 513 help_index : 'gd',
465 514 handler : function (event) {
466 515 IPython.quick_help.show_keyboard_shortcuts();
467 516 return false;
468 517 }
469 518 },
470 519 'z' : {
471 520 help : 'undo last delete',
472 521 help_index : 'ei',
473 522 handler : function (event) {
474 523 IPython.notebook.undelete_cell();
475 524 return false;
476 525 }
477 526 },
478 527 'shift+=' : {
479 528 help : 'merge cell below',
480 529 help_index : 'ek',
481 530 handler : function (event) {
482 531 IPython.notebook.merge_cell_below();
483 532 return false;
484 533 }
485 534 },
535 'shift+m' : {
536 help : 'merge cell below',
537 help_index : 'ek',
538 handler : function (event) {
539 IPython.notebook.merge_cell_below();
540 return false;
541 }
542 },
486 543 }
487 544
488 545
489 546 // Shortcut manager class
490 547
491 548 var ShortcutManager = function (delay) {
492 549 this._shortcuts = {}
493 550 this._counts = {}
494 551 this.delay = delay || 800; // delay in milliseconds
495 552 }
496 553
497 554 ShortcutManager.prototype.help = function () {
498 555 var help = [];
499 556 for (var shortcut in this._shortcuts) {
500 557 var help_string = this._shortcuts[shortcut]['help'];
501 558 var help_index = this._shortcuts[shortcut]['help_index'];
502 559 if (help_string) {
560 if (platform === 'MacOS') {
561 shortcut = shortcut.replace('meta', 'cmd');
562 }
503 563 help.push({
504 564 shortcut: shortcut,
505 565 help: help_string,
506 566 help_index: help_index}
507 567 );
508 568 }
509 569 }
510 570 help.sort(function (a, b) {
511 571 if (a.help_index > b.help_index)
512 572 return 1;
513 573 if (a.help_index < b.help_index)
514 574 return -1;
515 575 return 0;
516 576 });
517 577 return help;
518 578 }
519 579
520 580 ShortcutManager.prototype.normalize_key = function (key) {
521 581 return inv_keycodes[keycodes[key]];
522 582 }
523 583
524 584 ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
525 585 // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
586 shortcut = shortcut.replace('cmd', 'meta').toLowerCase();
526 587 var values = shortcut.split("+");
527 588 if (values.length === 1) {
528 589 return this.normalize_key(values[0])
529 590 } else {
530 591 var modifiers = values.slice(0,-1);
531 592 var key = this.normalize_key(values[values.length-1]);
532 593 modifiers.sort();
533 594 return modifiers.join('+') + '+' + key;
534 595 }
535 596 }
536 597
537 598 ShortcutManager.prototype.event_to_shortcut = function (event) {
538 599 // Convert a jQuery keyboard event to a strong based keyboard shortcut
539 600 var shortcut = '';
540 601 var key = inv_keycodes[event.which]
541 602 if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
542 603 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
543 604 if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
544 605 if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
545 606 shortcut += key;
546 607 return shortcut
547 608 }
548 609
549 610 ShortcutManager.prototype.clear_shortcuts = function () {
550 611 this._shortcuts = {};
551 612 }
552 613
553 614 ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
554 615 if (typeof(data) === 'function') {
555 616 data = {help: '', help_index: '', handler: data}
556 617 }
557 618 data.help_index = data.help_index || '';
558 619 data.help = data.help || '';
559 620 data.count = data.count || 1;
560 621 if (data.help_index === '') {
561 622 data.help_index = 'zz';
562 623 }
563 624 shortcut = this.normalize_shortcut(shortcut);
564 625 this._counts[shortcut] = 0;
565 626 this._shortcuts[shortcut] = data;
566 627 }
567 628
568 629 ShortcutManager.prototype.add_shortcuts = function (data) {
569 630 for (var shortcut in data) {
570 631 this.add_shortcut(shortcut, data[shortcut]);
571 632 }
572 633 }
573 634
574 635 ShortcutManager.prototype.remove_shortcut = function (shortcut) {
575 636 shortcut = this.normalize_shortcut(shortcut);
576 637 delete this._counts[shortcut];
577 638 delete this._shortcuts[shortcut];
578 639 }
579 640
580 641 ShortcutManager.prototype.count_handler = function (shortcut, event, handler) {
581 642 var that = this;
582 643 var c = this._counts;
583 644 if (c[shortcut] === 0) {
584 645 c[shortcut] = 1;
585 646 setTimeout(function () {
586 647 c[shortcut] = 0;
587 648 }, that.delay);
588 649 } else if (c[shortcut] === 1) {
589 650 c[shortcut] = 0;
590 651 return handler(event);
591 652 }
592 653 return false;
593 654
594 655 }
595 656
596 657 ShortcutManager.prototype.call_handler = function (event) {
597 658 var shortcut = this.event_to_shortcut(event);
598 659 var data = this._shortcuts[shortcut];
599 660 if (data) {
600 661 var handler = data['handler'];
601 662 if (handler) {
602 663 if (data.count === 1) {
603 664 return handler(event);
604 665 } else if (data.count > 1) {
605 666 return this.count_handler(shortcut, event, handler);
606 667 }
607 668 }
608 669 }
609 670 return true;
610 671 }
611 672
612 673
613 674
614 675 // Main keyboard manager for the notebook
615 676
616 677 var KeyboardManager = function () {
617 678 this.mode = 'command';
618 679 this.enabled = true;
619 680 this.bind_events();
620 681 this.command_shortcuts = new ShortcutManager();
621 682 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
622 683 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
623 684 this.edit_shortcuts = new ShortcutManager();
624 685 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
625 686 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
626 687 };
627 688
628 689 KeyboardManager.prototype.bind_events = function () {
629 690 var that = this;
630 691 $(document).keydown(function (event) {
631 692 return that.handle_keydown(event);
632 693 });
633 694 };
634 695
635 696 KeyboardManager.prototype.handle_keydown = function (event) {
636 697 var notebook = IPython.notebook;
637 698
638 699 if (event.which === keycodes['esc']) {
639 700 // Intercept escape at highest level to avoid closing
640 701 // websocket connection with firefox
641 702 event.preventDefault();
642 703 }
643 704
644 705 if (!this.enabled) {
645 706 if (event.which === keycodes['esc']) {
646 707 // ESC
647 708 notebook.command_mode();
648 709 return false;
649 710 }
650 711 return true;
651 712 }
652 713
653 714 if (this.mode === 'edit') {
654 715 return this.edit_shortcuts.call_handler(event);
655 716 } else if (this.mode === 'command') {
656 717 return this.command_shortcuts.call_handler(event);
657 718 }
658 719 return true;
659 720 }
660 721
661 722 KeyboardManager.prototype.edit_mode = function () {
662 723 this.last_mode = this.mode;
663 724 this.mode = 'edit';
664 725 }
665 726
666 727 KeyboardManager.prototype.command_mode = function () {
667 728 this.last_mode = this.mode;
668 729 this.mode = 'command';
669 730 }
670 731
671 732 KeyboardManager.prototype.enable = function () {
672 733 this.enabled = true;
673 734 }
674 735
675 736 KeyboardManager.prototype.disable = function () {
676 737 this.enabled = false;
677 738 }
678 739
679 740 KeyboardManager.prototype.register_events = function (e) {
680 741 var that = this;
681 742 e.on('focusin', function () {
682 743 that.command_mode();
683 744 that.disable();
684 745 });
685 746 e.on('focusout', function () {
686 747 that.command_mode();
687 748 that.enable();
688 749 });
689 750 // There are times (raw_input) where we remove the element from the DOM before
690 751 // focusout is called. In this case we bind to the remove event of jQueryUI,
691 752 // which gets triggered upon removal.
692 753 e.on('remove', function () {
693 754 that.command_mode();
694 755 that.enable();
695 756 });
696 757 }
697 758
698 759
699 760 IPython.keycodes = keycodes;
700 761 IPython.inv_keycodes = inv_keycodes;
701 762 IPython.default_common_shortcuts = default_common_shortcuts;
702 763 IPython.default_edit_shortcuts = default_edit_shortcuts;
703 764 IPython.default_command_shortcuts = default_command_shortcuts;
704 765 IPython.ShortcutManager = ShortcutManager;
705 766 IPython.KeyboardManager = KeyboardManager;
706 767
707 768 return IPython;
708 769
709 770 }(IPython));
@@ -1,210 +1,210
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2012 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notification widget
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14 var utils = IPython.utils;
15 15
16 16
17 17 var NotificationArea = function (selector) {
18 18 this.selector = selector;
19 19 if (this.selector !== undefined) {
20 20 this.element = $(selector);
21 21 }
22 22 this.widget_dict = {};
23 23 };
24 24
25 25 NotificationArea.prototype.temp_message = function (msg, timeout, css_class) {
26 26 var uuid = utils.uuid();
27 27 if( css_class == 'danger') {css_class = 'ui-state-error';}
28 28 if( css_class == 'warning') {css_class = 'ui-state-highlight';}
29 29 var tdiv = $('<div>')
30 30 .attr('id',uuid)
31 31 .addClass('notification_widget ui-widget ui-widget-content ui-corner-all')
32 32 .addClass('border-box-sizing')
33 33 .addClass(css_class)
34 34 .hide()
35 35 .text(msg);
36 36
37 37 $(this.selector).append(tdiv);
38 38 var tmout = Math.max(1500,(timeout||1500));
39 39 tdiv.fadeIn(100);
40 40
41 41 setTimeout(function () {
42 42 tdiv.fadeOut(100, function () {tdiv.remove();});
43 43 }, tmout);
44 44 };
45 45
46 46 NotificationArea.prototype.widget = function(name) {
47 47 if(this.widget_dict[name] == undefined) {
48 48 return this.new_notification_widget(name);
49 49 }
50 50 return this.get_widget(name);
51 51 };
52 52
53 53 NotificationArea.prototype.get_widget = function(name) {
54 54 if(this.widget_dict[name] == undefined) {
55 55 throw('no widgets with this name');
56 56 }
57 57 return this.widget_dict[name];
58 58 };
59 59
60 60 NotificationArea.prototype.new_notification_widget = function(name) {
61 61 if(this.widget_dict[name] != undefined) {
62 62 throw('widget with that name already exists ! ');
63 63 }
64 64 var div = $('<div/>').attr('id','notification_'+name);
65 65 $(this.selector).append(div);
66 66 this.widget_dict[name] = new IPython.NotificationWidget('#notification_'+name);
67 67 return this.widget_dict[name];
68 68 };
69 69
70 70 NotificationArea.prototype.init_notification_widgets = function() {
71 71 var knw = this.new_notification_widget('kernel');
72 72
73 73 // Kernel events
74 74 $([IPython.events]).on('status_idle.Kernel',function () {
75 75 IPython.save_widget.update_document_title();
76 76 knw.set_message('Kernel Idle',200);
77 77 }
78 78 );
79 79
80 80 $([IPython.events]).on('status_busy.Kernel',function () {
81 81 window.document.title='(Busy) '+window.document.title;
82 82 knw.set_message("Kernel busy");
83 83 });
84 84
85 85 $([IPython.events]).on('status_restarting.Kernel',function () {
86 86 IPython.save_widget.update_document_title();
87 87 knw.set_message("Restarting kernel", 2000);
88 88 });
89 89
90 90 $([IPython.events]).on('status_interrupting.Kernel',function () {
91 knw.set_message("Interrupting kernel");
91 knw.set_message("Interrupting kernel", 2000);
92 92 });
93 93
94 94 $([IPython.events]).on('status_dead.Kernel',function () {
95 95 var msg = 'The kernel has died, and the automatic restart has failed.' +
96 96 ' It is possible the kernel cannot be restarted.' +
97 97 ' If you are not able to restart the kernel, you will still be able to save' +
98 98 ' the notebook, but running code will no longer work until the notebook' +
99 99 ' is reopened.';
100 100
101 101 IPython.dialog.modal({
102 102 title: "Dead kernel",
103 103 body : msg,
104 104 buttons : {
105 105 "Manual Restart": {
106 106 class: "btn-danger",
107 107 click: function () {
108 108 $([IPython.events]).trigger('status_restarting.Kernel');
109 109 IPython.notebook.start_kernel();
110 110 }
111 111 },
112 112 "Don't restart": {}
113 113 }
114 114 });
115 115 });
116 116
117 117 $([IPython.events]).on('websocket_closed.Kernel', function (event, data) {
118 118 var kernel = data.kernel;
119 119 var ws_url = data.ws_url;
120 120 var early = data.early;
121 121 var msg;
122 122 if (!early) {
123 123 knw.set_message('Reconnecting WebSockets', 1000);
124 124 setTimeout(function () {
125 125 kernel.start_channels();
126 126 }, 5000);
127 127 return;
128 128 }
129 129 console.log('WebSocket connection failed: ', ws_url)
130 130 msg = "A WebSocket connection to could not be established." +
131 131 " You will NOT be able to run code. Check your" +
132 132 " network connection or notebook server configuration.";
133 133 IPython.dialog.modal({
134 134 title: "WebSocket connection failed",
135 135 body: msg,
136 136 buttons : {
137 137 "OK": {},
138 138 "Reconnect": {
139 139 click: function () {
140 140 knw.set_message('Reconnecting WebSockets', 1000);
141 141 setTimeout(function () {
142 142 kernel.start_channels();
143 143 }, 5000);
144 144 }
145 145 }
146 146 }
147 147 });
148 148 });
149 149
150 150
151 151 var nnw = this.new_notification_widget('notebook');
152 152
153 153 // Notebook events
154 154 $([IPython.events]).on('notebook_loading.Notebook', function () {
155 155 nnw.set_message("Loading notebook",500);
156 156 });
157 157 $([IPython.events]).on('notebook_loaded.Notebook', function () {
158 158 nnw.set_message("Notebook loaded",500);
159 159 });
160 160 $([IPython.events]).on('notebook_saving.Notebook', function () {
161 161 nnw.set_message("Saving notebook",500);
162 162 });
163 163 $([IPython.events]).on('notebook_saved.Notebook', function () {
164 164 nnw.set_message("Notebook saved",2000);
165 165 });
166 166 $([IPython.events]).on('notebook_save_failed.Notebook', function () {
167 167 nnw.set_message("Notebook save failed");
168 168 });
169 169
170 170 // Checkpoint events
171 171 $([IPython.events]).on('checkpoint_created.Notebook', function (evt, data) {
172 172 var msg = "Checkpoint created";
173 173 if (data.last_modified) {
174 174 var d = new Date(data.last_modified);
175 175 msg = msg + ": " + d.format("HH:MM:ss");
176 176 }
177 177 nnw.set_message(msg, 2000);
178 178 });
179 179 $([IPython.events]).on('checkpoint_failed.Notebook', function () {
180 180 nnw.set_message("Checkpoint failed");
181 181 });
182 182 $([IPython.events]).on('checkpoint_deleted.Notebook', function () {
183 183 nnw.set_message("Checkpoint deleted", 500);
184 184 });
185 185 $([IPython.events]).on('checkpoint_delete_failed.Notebook', function () {
186 186 nnw.set_message("Checkpoint delete failed");
187 187 });
188 188 $([IPython.events]).on('checkpoint_restoring.Notebook', function () {
189 189 nnw.set_message("Restoring to checkpoint...", 500);
190 190 });
191 191 $([IPython.events]).on('checkpoint_restore_failed.Notebook', function () {
192 192 nnw.set_message("Checkpoint restore failed");
193 193 });
194 194
195 195 // Autosave events
196 196 $([IPython.events]).on('autosave_disabled.Notebook', function () {
197 197 nnw.set_message("Autosave disabled", 2000);
198 198 });
199 199 $([IPython.events]).on('autosave_enabled.Notebook', function (evt, interval) {
200 200 nnw.set_message("Saving every " + interval / 1000 + "s", 1000);
201 201 });
202 202
203 203 };
204 204
205 205 IPython.NotificationArea = NotificationArea;
206 206
207 207 return IPython;
208 208
209 209 }(IPython));
210 210
General Comments 0
You need to be logged in to leave comments. Login now