##// END OF EJS Templates
use es6
Jonathan Frederic -
Show More
@@ -1,761 +1,760
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'codemirror/lib/codemirror',
8 'rsvp',
9 ], function(IPython, $, CodeMirror, rsvp){
8 ], function(IPython, $, CodeMirror){
10 9 "use strict";
11 10
12 11 IPython.load_extensions = function () {
13 12 // load one or more IPython notebook extensions with requirejs
14 13
15 14 var extensions = [];
16 15 var extension_names = arguments;
17 16 for (var i = 0; i < extension_names.length; i++) {
18 17 extensions.push("nbextensions/" + arguments[i]);
19 18 }
20 19
21 20 require(extensions,
22 21 function () {
23 22 for (var i = 0; i < arguments.length; i++) {
24 23 var ext = arguments[i];
25 24 var ext_name = extension_names[i];
26 25 // success callback
27 26 console.log("Loaded extension: " + ext_name);
28 27 if (ext && ext.load_ipython_extension !== undefined) {
29 28 ext.load_ipython_extension();
30 29 }
31 30 }
32 31 },
33 32 function (err) {
34 33 // failure callback
35 34 console.log("Failed to load extension(s):", err.requireModules, err);
36 35 }
37 36 );
38 37 };
39 38
40 39 //============================================================================
41 40 // Cross-browser RegEx Split
42 41 //============================================================================
43 42
44 43 // This code has been MODIFIED from the code licensed below to not replace the
45 44 // default browser split. The license is reproduced here.
46 45
47 46 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
48 47 /*!
49 48 * Cross-Browser Split 1.1.1
50 49 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
51 50 * Available under the MIT License
52 51 * ECMAScript compliant, uniform cross-browser split method
53 52 */
54 53
55 54 /**
56 55 * Splits a string into an array of strings using a regex or string
57 56 * separator. Matches of the separator are not included in the result array.
58 57 * However, if `separator` is a regex that contains capturing groups,
59 58 * backreferences are spliced into the result each time `separator` is
60 59 * matched. Fixes browser bugs compared to the native
61 60 * `String.prototype.split` and can be used reliably cross-browser.
62 61 * @param {String} str String to split.
63 62 * @param {RegExp|String} separator Regex or string to use for separating
64 63 * the string.
65 64 * @param {Number} [limit] Maximum number of items to include in the result
66 65 * array.
67 66 * @returns {Array} Array of substrings.
68 67 * @example
69 68 *
70 69 * // Basic use
71 70 * regex_split('a b c d', ' ');
72 71 * // -> ['a', 'b', 'c', 'd']
73 72 *
74 73 * // With limit
75 74 * regex_split('a b c d', ' ', 2);
76 75 * // -> ['a', 'b']
77 76 *
78 77 * // Backreferences in result array
79 78 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
80 79 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
81 80 */
82 81 var regex_split = function (str, separator, limit) {
83 82 // If `separator` is not a regex, use `split`
84 83 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
85 84 return split.call(str, separator, limit);
86 85 }
87 86 var output = [],
88 87 flags = (separator.ignoreCase ? "i" : "") +
89 88 (separator.multiline ? "m" : "") +
90 89 (separator.extended ? "x" : "") + // Proposed for ES6
91 90 (separator.sticky ? "y" : ""), // Firefox 3+
92 91 lastLastIndex = 0,
93 92 // Make `global` and avoid `lastIndex` issues by working with a copy
94 93 separator = new RegExp(separator.source, flags + "g"),
95 94 separator2, match, lastIndex, lastLength;
96 95 str += ""; // Type-convert
97 96
98 97 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
99 98 if (!compliantExecNpcg) {
100 99 // Doesn't need flags gy, but they don't hurt
101 100 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
102 101 }
103 102 /* Values for `limit`, per the spec:
104 103 * If undefined: 4294967295 // Math.pow(2, 32) - 1
105 104 * If 0, Infinity, or NaN: 0
106 105 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
107 106 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
108 107 * If other: Type-convert, then use the above rules
109 108 */
110 109 limit = typeof(limit) === "undefined" ?
111 110 -1 >>> 0 : // Math.pow(2, 32) - 1
112 111 limit >>> 0; // ToUint32(limit)
113 112 while (match = separator.exec(str)) {
114 113 // `separator.lastIndex` is not reliable cross-browser
115 114 lastIndex = match.index + match[0].length;
116 115 if (lastIndex > lastLastIndex) {
117 116 output.push(str.slice(lastLastIndex, match.index));
118 117 // Fix browsers whose `exec` methods don't consistently return `undefined` for
119 118 // nonparticipating capturing groups
120 119 if (!compliantExecNpcg && match.length > 1) {
121 120 match[0].replace(separator2, function () {
122 121 for (var i = 1; i < arguments.length - 2; i++) {
123 122 if (typeof(arguments[i]) === "undefined") {
124 123 match[i] = undefined;
125 124 }
126 125 }
127 126 });
128 127 }
129 128 if (match.length > 1 && match.index < str.length) {
130 129 Array.prototype.push.apply(output, match.slice(1));
131 130 }
132 131 lastLength = match[0].length;
133 132 lastLastIndex = lastIndex;
134 133 if (output.length >= limit) {
135 134 break;
136 135 }
137 136 }
138 137 if (separator.lastIndex === match.index) {
139 138 separator.lastIndex++; // Avoid an infinite loop
140 139 }
141 140 }
142 141 if (lastLastIndex === str.length) {
143 142 if (lastLength || !separator.test("")) {
144 143 output.push("");
145 144 }
146 145 } else {
147 146 output.push(str.slice(lastLastIndex));
148 147 }
149 148 return output.length > limit ? output.slice(0, limit) : output;
150 149 };
151 150
152 151 //============================================================================
153 152 // End contributed Cross-browser RegEx Split
154 153 //============================================================================
155 154
156 155
157 156 var uuid = function () {
158 157 // http://www.ietf.org/rfc/rfc4122.txt
159 158 var s = [];
160 159 var hexDigits = "0123456789ABCDEF";
161 160 for (var i = 0; i < 32; i++) {
162 161 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
163 162 }
164 163 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
165 164 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
166 165
167 166 var uuid = s.join("");
168 167 return uuid;
169 168 };
170 169
171 170
172 171 //Fix raw text to parse correctly in crazy XML
173 172 function xmlencode(string) {
174 173 return string.replace(/\&/g,'&'+'amp;')
175 174 .replace(/</g,'&'+'lt;')
176 175 .replace(/>/g,'&'+'gt;')
177 176 .replace(/\'/g,'&'+'apos;')
178 177 .replace(/\"/g,'&'+'quot;')
179 178 .replace(/`/g,'&'+'#96;');
180 179 }
181 180
182 181
183 182 //Map from terminal commands to CSS classes
184 183 var ansi_colormap = {
185 184 "01":"ansibold",
186 185
187 186 "30":"ansiblack",
188 187 "31":"ansired",
189 188 "32":"ansigreen",
190 189 "33":"ansiyellow",
191 190 "34":"ansiblue",
192 191 "35":"ansipurple",
193 192 "36":"ansicyan",
194 193 "37":"ansigray",
195 194
196 195 "40":"ansibgblack",
197 196 "41":"ansibgred",
198 197 "42":"ansibggreen",
199 198 "43":"ansibgyellow",
200 199 "44":"ansibgblue",
201 200 "45":"ansibgpurple",
202 201 "46":"ansibgcyan",
203 202 "47":"ansibggray"
204 203 };
205 204
206 205 function _process_numbers(attrs, numbers) {
207 206 // process ansi escapes
208 207 var n = numbers.shift();
209 208 if (ansi_colormap[n]) {
210 209 if ( ! attrs["class"] ) {
211 210 attrs["class"] = ansi_colormap[n];
212 211 } else {
213 212 attrs["class"] += " " + ansi_colormap[n];
214 213 }
215 214 } else if (n == "38" || n == "48") {
216 215 // VT100 256 color or 24 bit RGB
217 216 if (numbers.length < 2) {
218 217 console.log("Not enough fields for VT100 color", numbers);
219 218 return;
220 219 }
221 220
222 221 var index_or_rgb = numbers.shift();
223 222 var r,g,b;
224 223 if (index_or_rgb == "5") {
225 224 // 256 color
226 225 var idx = parseInt(numbers.shift());
227 226 if (idx < 16) {
228 227 // indexed ANSI
229 228 // ignore bright / non-bright distinction
230 229 idx = idx % 8;
231 230 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
232 231 if ( ! attrs["class"] ) {
233 232 attrs["class"] = ansiclass;
234 233 } else {
235 234 attrs["class"] += " " + ansiclass;
236 235 }
237 236 return;
238 237 } else if (idx < 232) {
239 238 // 216 color 6x6x6 RGB
240 239 idx = idx - 16;
241 240 b = idx % 6;
242 241 g = Math.floor(idx / 6) % 6;
243 242 r = Math.floor(idx / 36) % 6;
244 243 // convert to rgb
245 244 r = (r * 51);
246 245 g = (g * 51);
247 246 b = (b * 51);
248 247 } else {
249 248 // grayscale
250 249 idx = idx - 231;
251 250 // it's 1-24 and should *not* include black or white,
252 251 // so a 26 point scale
253 252 r = g = b = Math.floor(idx * 256 / 26);
254 253 }
255 254 } else if (index_or_rgb == "2") {
256 255 // Simple 24 bit RGB
257 256 if (numbers.length > 3) {
258 257 console.log("Not enough fields for RGB", numbers);
259 258 return;
260 259 }
261 260 r = numbers.shift();
262 261 g = numbers.shift();
263 262 b = numbers.shift();
264 263 } else {
265 264 console.log("unrecognized control", numbers);
266 265 return;
267 266 }
268 267 if (r !== undefined) {
269 268 // apply the rgb color
270 269 var line;
271 270 if (n == "38") {
272 271 line = "color: ";
273 272 } else {
274 273 line = "background-color: ";
275 274 }
276 275 line = line + "rgb(" + r + "," + g + "," + b + ");";
277 276 if ( !attrs.style ) {
278 277 attrs.style = line;
279 278 } else {
280 279 attrs.style += " " + line;
281 280 }
282 281 }
283 282 }
284 283 }
285 284
286 285 function ansispan(str) {
287 286 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
288 287 // regular ansi escapes (using the table above)
289 288 var is_open = false;
290 289 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
291 290 if (!pattern) {
292 291 // [(01|22|39|)m close spans
293 292 if (is_open) {
294 293 is_open = false;
295 294 return "</span>";
296 295 } else {
297 296 return "";
298 297 }
299 298 } else {
300 299 is_open = true;
301 300
302 301 // consume sequence of color escapes
303 302 var numbers = pattern.match(/\d+/g);
304 303 var attrs = {};
305 304 while (numbers.length > 0) {
306 305 _process_numbers(attrs, numbers);
307 306 }
308 307
309 308 var span = "<span ";
310 309 for (var attr in attrs) {
311 310 var value = attrs[attr];
312 311 span = span + " " + attr + '="' + attrs[attr] + '"';
313 312 }
314 313 return span + ">";
315 314 }
316 315 });
317 316 }
318 317
319 318 // Transform ANSI color escape codes into HTML <span> tags with css
320 319 // classes listed in the above ansi_colormap object. The actual color used
321 320 // are set in the css file.
322 321 function fixConsole(txt) {
323 322 txt = xmlencode(txt);
324 323 var re = /\033\[([\dA-Fa-f;]*?)m/;
325 324 var opened = false;
326 325 var cmds = [];
327 326 var opener = "";
328 327 var closer = "";
329 328
330 329 // Strip all ANSI codes that are not color related. Matches
331 330 // all ANSI codes that do not end with "m".
332 331 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
333 332 txt = txt.replace(ignored_re, "");
334 333
335 334 // color ansi codes
336 335 txt = ansispan(txt);
337 336 return txt;
338 337 }
339 338
340 339 // Remove chunks that should be overridden by the effect of
341 340 // carriage return characters
342 341 function fixCarriageReturn(txt) {
343 342 var tmp = txt;
344 343 do {
345 344 txt = tmp;
346 345 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
347 346 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
348 347 } while (tmp.length < txt.length);
349 348 return txt;
350 349 }
351 350
352 351 // Locate any URLs and convert them to a anchor tag
353 352 function autoLinkUrls(txt) {
354 353 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
355 354 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
356 355 }
357 356
358 357 var points_to_pixels = function (points) {
359 358 // A reasonably good way of converting between points and pixels.
360 359 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
361 360 $(body).append(test);
362 361 var pixel_per_point = test.width()/10000;
363 362 test.remove();
364 363 return Math.floor(points*pixel_per_point);
365 364 };
366 365
367 366 var always_new = function (constructor) {
368 367 // wrapper around contructor to avoid requiring `var a = new constructor()`
369 368 // useful for passing constructors as callbacks,
370 369 // not for programmer laziness.
371 370 // from http://programmers.stackexchange.com/questions/118798
372 371 return function () {
373 372 var obj = Object.create(constructor.prototype);
374 373 constructor.apply(obj, arguments);
375 374 return obj;
376 375 };
377 376 };
378 377
379 378 var url_path_join = function () {
380 379 // join a sequence of url components with '/'
381 380 var url = '';
382 381 for (var i = 0; i < arguments.length; i++) {
383 382 if (arguments[i] === '') {
384 383 continue;
385 384 }
386 385 if (url.length > 0 && url[url.length-1] != '/') {
387 386 url = url + '/' + arguments[i];
388 387 } else {
389 388 url = url + arguments[i];
390 389 }
391 390 }
392 391 url = url.replace(/\/\/+/, '/');
393 392 return url;
394 393 };
395 394
396 395 var url_path_split = function (path) {
397 396 // Like os.path.split for URLs.
398 397 // Always returns two strings, the directory path and the base filename
399 398
400 399 var idx = path.lastIndexOf('/');
401 400 if (idx === -1) {
402 401 return ['', path];
403 402 } else {
404 403 return [ path.slice(0, idx), path.slice(idx + 1) ];
405 404 }
406 405 };
407 406
408 407 var parse_url = function (url) {
409 408 // an `a` element with an href allows attr-access to the parsed segments of a URL
410 409 // a = parse_url("http://localhost:8888/path/name#hash")
411 410 // a.protocol = "http:"
412 411 // a.host = "localhost:8888"
413 412 // a.hostname = "localhost"
414 413 // a.port = 8888
415 414 // a.pathname = "/path/name"
416 415 // a.hash = "#hash"
417 416 var a = document.createElement("a");
418 417 a.href = url;
419 418 return a;
420 419 };
421 420
422 421 var encode_uri_components = function (uri) {
423 422 // encode just the components of a multi-segment uri,
424 423 // leaving '/' separators
425 424 return uri.split('/').map(encodeURIComponent).join('/');
426 425 };
427 426
428 427 var url_join_encode = function () {
429 428 // join a sequence of url components with '/',
430 429 // encoding each component with encodeURIComponent
431 430 return encode_uri_components(url_path_join.apply(null, arguments));
432 431 };
433 432
434 433
435 434 var splitext = function (filename) {
436 435 // mimic Python os.path.splitext
437 436 // Returns ['base', '.ext']
438 437 var idx = filename.lastIndexOf('.');
439 438 if (idx > 0) {
440 439 return [filename.slice(0, idx), filename.slice(idx)];
441 440 } else {
442 441 return [filename, ''];
443 442 }
444 443 };
445 444
446 445
447 446 var escape_html = function (text) {
448 447 // escape text to HTML
449 448 return $("<div/>").text(text).html();
450 449 };
451 450
452 451
453 452 var get_body_data = function(key) {
454 453 // get a url-encoded item from body.data and decode it
455 454 // we should never have any encoded URLs anywhere else in code
456 455 // until we are building an actual request
457 456 return decodeURIComponent($('body').data(key));
458 457 };
459 458
460 459 var to_absolute_cursor_pos = function (cm, cursor) {
461 460 // get the absolute cursor position from CodeMirror's col, ch
462 461 if (!cursor) {
463 462 cursor = cm.getCursor();
464 463 }
465 464 var cursor_pos = cursor.ch;
466 465 for (var i = 0; i < cursor.line; i++) {
467 466 cursor_pos += cm.getLine(i).length + 1;
468 467 }
469 468 return cursor_pos;
470 469 };
471 470
472 471 var from_absolute_cursor_pos = function (cm, cursor_pos) {
473 472 // turn absolute cursor postion into CodeMirror col, ch cursor
474 473 var i, line;
475 474 var offset = 0;
476 475 for (i = 0, line=cm.getLine(i); line !== undefined; i++, line=cm.getLine(i)) {
477 476 if (offset + line.length < cursor_pos) {
478 477 offset += line.length + 1;
479 478 } else {
480 479 return {
481 480 line : i,
482 481 ch : cursor_pos - offset,
483 482 };
484 483 }
485 484 }
486 485 // reached end, return endpoint
487 486 return {
488 487 ch : line.length - 1,
489 488 line : i - 1,
490 489 };
491 490 };
492 491
493 492 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
494 493 var browser = (function() {
495 494 if (typeof navigator === 'undefined') {
496 495 // navigator undefined in node
497 496 return 'None';
498 497 }
499 498 var N= navigator.appName, ua= navigator.userAgent, tem;
500 499 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
501 500 if (M && (tem= ua.match(/version\/([\.\d]+)/i)) !== null) M[2]= tem[1];
502 501 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
503 502 return M;
504 503 })();
505 504
506 505 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
507 506 var platform = (function () {
508 507 if (typeof navigator === 'undefined') {
509 508 // navigator undefined in node
510 509 return 'None';
511 510 }
512 511 var OSName="None";
513 512 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
514 513 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
515 514 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
516 515 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
517 516 return OSName;
518 517 })();
519 518
520 519 var is_or_has = function (a, b) {
521 520 // Is b a child of a or a itself?
522 521 return a.has(b).length !==0 || a.is(b);
523 522 };
524 523
525 524 var is_focused = function (e) {
526 525 // Is element e, or one of its children focused?
527 526 e = $(e);
528 527 var target = $(document.activeElement);
529 528 if (target.length > 0) {
530 529 if (is_or_has(e, target)) {
531 530 return true;
532 531 } else {
533 532 return false;
534 533 }
535 534 } else {
536 535 return false;
537 536 }
538 537 };
539 538
540 539 var mergeopt = function(_class, options, overwrite){
541 540 options = options || {};
542 541 overwrite = overwrite || {};
543 542 return $.extend(true, {}, _class.options_default, options, overwrite);
544 543 };
545 544
546 545 var ajax_error_msg = function (jqXHR) {
547 546 // Return a JSON error message if there is one,
548 547 // otherwise the basic HTTP status text.
549 548 if (jqXHR.responseJSON && jqXHR.responseJSON.traceback) {
550 549 return jqXHR.responseJSON.traceback;
551 550 } else if (jqXHR.responseJSON && jqXHR.responseJSON.message) {
552 551 return jqXHR.responseJSON.message;
553 552 } else {
554 553 return jqXHR.statusText;
555 554 }
556 555 };
557 556 var log_ajax_error = function (jqXHR, status, error) {
558 557 // log ajax failures with informative messages
559 558 var msg = "API request failed (" + jqXHR.status + "): ";
560 559 console.log(jqXHR);
561 560 msg += ajax_error_msg(jqXHR);
562 561 console.log(msg);
563 562 };
564 563
565 564 var requireCodeMirrorMode = function (mode, callback, errback) {
566 565 // load a mode with requirejs
567 566 if (typeof mode != "string") mode = mode.name;
568 567 if (CodeMirror.modes.hasOwnProperty(mode)) {
569 568 callback(CodeMirror.modes.mode);
570 569 return;
571 570 }
572 571 require([
573 572 // might want to use CodeMirror.modeURL here
574 573 ['codemirror/mode', mode, mode].join('/'),
575 574 ], callback, errback
576 575 );
577 576 };
578 577
579 578 /** Error type for wrapped XHR errors. */
580 579 var XHR_ERROR = 'XhrError';
581 580
582 581 /**
583 582 * Wraps an AJAX error as an Error object.
584 583 */
585 584 var wrap_ajax_error = function (jqXHR, status, error) {
586 585 var wrapped_error = new Error(ajax_error_msg(jqXHR));
587 586 wrapped_error.name = XHR_ERROR;
588 587 // provide xhr response
589 588 wrapped_error.xhr = jqXHR;
590 589 wrapped_error.xhr_status = status;
591 590 wrapped_error.xhr_error = error;
592 591 return wrapped_error;
593 592 };
594 593
595 594 var promising_ajax = function(url, settings) {
596 595 // Like $.ajax, but returning an ES6 promise. success and error settings
597 596 // will be ignored.
598 597 return new Promise(function(resolve, reject) {
599 598 settings.success = function(data, status, jqXHR) {
600 599 resolve(data);
601 600 };
602 601 settings.error = function(jqXHR, status, error) {
603 602 log_ajax_error(jqXHR, status, error);
604 603 reject(wrap_ajax_error(jqXHR, status, error));
605 604 };
606 605 $.ajax(url, settings);
607 606 });
608 607 };
609 608
610 609 var WrappedError = function(message, error){
611 610 // Wrappable Error class
612 611
613 612 // The Error class doesn't actually act on `this`. Instead it always
614 613 // returns a new instance of Error. Here we capture that instance so we
615 614 // can apply it's properties to `this`.
616 615 var tmp = Error.apply(this, [message]);
617 616
618 617 // Copy the properties of the error over to this.
619 618 var properties = Object.getOwnPropertyNames(tmp);
620 619 for (var i = 0; i < properties.length; i++) {
621 620 this[properties[i]] = tmp[properties[i]];
622 621 }
623 622
624 623 // Keep a stack of the original error messages.
625 624 if (error instanceof WrappedError) {
626 625 this.error_stack = error.error_stack;
627 626 } else {
628 627 this.error_stack = [error];
629 628 }
630 629 this.error_stack.push(tmp);
631 630
632 631 return this;
633 632 };
634 633
635 634 WrappedError.prototype = Object.create(Error.prototype, {});
636 635
637 636
638 637 var load_class = function(class_name, module_name, registry) {
639 638 // Tries to load a class
640 639 //
641 640 // Tries to load a class from a module using require.js, if a module
642 641 // is specified, otherwise tries to load a class from the global
643 642 // registry, if the global registry is provided.
644 return new rsvp.Promise(function(resolve, reject) {
643 return new Promise(function(resolve, reject) {
645 644
646 645 // Try loading the view module using require.js
647 646 if (module_name) {
648 647 require([module_name], function(module) {
649 648 if (module[class_name] === undefined) {
650 649 reject(new Error('Class '+class_name+' not found in module '+module_name));
651 650 } else {
652 651 resolve(module[class_name]);
653 652 }
654 653 }, reject);
655 654 } else {
656 655 if (registry && registry[class_name]) {
657 656 resolve(registry[class_name]);
658 657 } else {
659 658 reject(new Error('Class '+class_name+' not found in registry '));
660 659 }
661 660 }
662 661 });
663 662 };
664 663
665 664 var resolve_dict = function(d) {
666 665 // Resolve a promiseful dictionary.
667 // Returns a single rsvp.Promise.
666 // Returns a single Promise.
668 667 var keys = Object.keys(d);
669 668 var values = [];
670 669 keys.forEach(function(key) {
671 670 values.push(d[key]);
672 671 });
673 return rsvp.Promise.all(values).then(function(v) {
672 return Promise.all(values).then(function(v) {
674 673 d = {};
675 674 for(var i=0; i<keys.length; i++) {
676 675 d[keys[i]] = v[i];
677 676 }
678 677 return d;
679 678 });
680 679 };
681 680
682 681 var WrappedError = function(message, error){
683 682 // Wrappable Error class
684 683
685 684 // The Error class doesn't actually act on `this`. Instead it always
686 685 // returns a new instance of Error. Here we capture that instance so we
687 686 // can apply it's properties to `this`.
688 687 var tmp = Error.apply(this, [message]);
689 688
690 689 // Copy the properties of the error over to this.
691 690 var properties = Object.getOwnPropertyNames(tmp);
692 691 for (var i = 0; i < properties.length; i++) {
693 692 this[properties[i]] = tmp[properties[i]];
694 693 }
695 694
696 695 // Keep a stack of the original error messages.
697 696 if (error instanceof WrappedError) {
698 697 this.error_stack = error.error_stack;
699 698 } else {
700 699 this.error_stack = [error];
701 700 }
702 701 this.error_stack.push(tmp);
703 702
704 703 return this;
705 704 };
706 705
707 706 WrappedError.prototype = Object.create(Error.prototype, {});
708 707
709 708 var reject = function(message, log) {
710 // Creates a wrappable rsvp.Promise rejection function.
709 // Creates a wrappable Promise rejection function.
711 710 //
712 // Creates a function that returns a rsvp.Promise.reject with a new WrappedError
711 // Creates a function that returns a Promise.reject with a new WrappedError
713 712 // that has the provided message and wraps the original error that
714 713 // caused the promise to reject.
715 714 return function(error) {
716 715 var wrapped_error = new WrappedError(message, error);
717 716 if (log) console.error(wrapped_error);
718 return rsvp.Promise.reject(wrapped_error);
717 return Promise.reject(wrapped_error);
719 718 };
720 719 };
721 720
722 721 var utils = {
723 722 regex_split : regex_split,
724 723 uuid : uuid,
725 724 fixConsole : fixConsole,
726 725 fixCarriageReturn : fixCarriageReturn,
727 726 autoLinkUrls : autoLinkUrls,
728 727 points_to_pixels : points_to_pixels,
729 728 get_body_data : get_body_data,
730 729 parse_url : parse_url,
731 730 url_path_split : url_path_split,
732 731 url_path_join : url_path_join,
733 732 url_join_encode : url_join_encode,
734 733 encode_uri_components : encode_uri_components,
735 734 splitext : splitext,
736 735 escape_html : escape_html,
737 736 always_new : always_new,
738 737 to_absolute_cursor_pos : to_absolute_cursor_pos,
739 738 from_absolute_cursor_pos : from_absolute_cursor_pos,
740 739 browser : browser,
741 740 platform: platform,
742 741 is_or_has : is_or_has,
743 742 is_focused : is_focused,
744 743 mergeopt: mergeopt,
745 744 ajax_error_msg : ajax_error_msg,
746 745 log_ajax_error : log_ajax_error,
747 746 requireCodeMirrorMode : requireCodeMirrorMode,
748 747 XHR_ERROR : XHR_ERROR,
749 748 wrap_ajax_error : wrap_ajax_error,
750 749 promising_ajax : promising_ajax,
751 750 WrappedError: WrappedError,
752 751 load_class: load_class,
753 752 resolve_dict: resolve_dict,
754 753 reject: reject,
755 754 };
756 755
757 756 // Backwards compatability.
758 757 IPython.utils = utils;
759 758
760 759 return utils;
761 760 });
@@ -1,156 +1,157
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 require([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'notebook/js/notebook',
8 8 'contents',
9 9 'base/js/utils',
10 10 'base/js/page',
11 11 'notebook/js/layoutmanager',
12 12 'base/js/events',
13 13 'auth/js/loginwidget',
14 14 'notebook/js/maintoolbar',
15 15 'notebook/js/pager',
16 16 'notebook/js/quickhelp',
17 17 'notebook/js/menubar',
18 18 'notebook/js/notificationarea',
19 19 'notebook/js/savewidget',
20 20 'notebook/js/keyboardmanager',
21 21 'notebook/js/config',
22 22 'notebook/js/kernelselector',
23 23 'codemirror/lib/codemirror',
24 24 'notebook/js/about',
25 25 // only loaded, not used, please keep sure this is loaded last
26 26 'custom/custom'
27 27 ], function(
28 28 IPython,
29 29 $,
30 30 notebook,
31 31 contents,
32 32 utils,
33 33 page,
34 34 layoutmanager,
35 35 events,
36 36 loginwidget,
37 37 maintoolbar,
38 38 pager,
39 39 quickhelp,
40 40 menubar,
41 41 notificationarea,
42 42 savewidget,
43 43 keyboardmanager,
44 44 config,
45 45 kernelselector,
46 46 CodeMirror,
47 47 about,
48 48 // please keep sure that even if not used, this is loaded last
49 49 custom
50 50 ) {
51 51 "use strict";
52 console.log(promise);
52 53
53 54 // compat with old IPython, remove for IPython > 3.0
54 55 window.CodeMirror = CodeMirror;
55 56
56 57 var common_options = {
57 58 ws_url : utils.get_body_data("wsUrl"),
58 59 base_url : utils.get_body_data("baseUrl"),
59 60 notebook_path : utils.get_body_data("notebookPath"),
60 61 notebook_name : utils.get_body_data('notebookName')
61 62 };
62 63
63 64 var user_config = $.extend({}, config.default_config);
64 65 var page = new page.Page();
65 66 var layout_manager = new layoutmanager.LayoutManager();
66 67 var pager = new pager.Pager('div#pager', 'div#pager_splitter', {
67 68 layout_manager: layout_manager,
68 69 events: events});
69 70 var keyboard_manager = new keyboardmanager.KeyboardManager({
70 71 pager: pager,
71 72 events: events});
72 73 var save_widget = new savewidget.SaveWidget('span#save_widget', {
73 74 events: events,
74 75 keyboard_manager: keyboard_manager});
75 76 var contents = new contents.Contents($.extend({
76 77 events: events},
77 78 common_options));
78 79 var notebook = new notebook.Notebook('div#notebook', $.extend({
79 80 events: events,
80 81 keyboard_manager: keyboard_manager,
81 82 save_widget: save_widget,
82 83 contents: contents,
83 84 config: user_config},
84 85 common_options));
85 86 var login_widget = new loginwidget.LoginWidget('span#login_widget', common_options);
86 87 var toolbar = new maintoolbar.MainToolBar('#maintoolbar-container', {
87 88 notebook: notebook,
88 89 events: events});
89 90 var quick_help = new quickhelp.QuickHelp({
90 91 keyboard_manager: keyboard_manager,
91 92 events: events,
92 93 notebook: notebook});
93 94 var menubar = new menubar.MenuBar('#menubar', $.extend({
94 95 notebook: notebook,
95 96 contents: contents,
96 97 layout_manager: layout_manager,
97 98 events: events,
98 99 save_widget: save_widget,
99 100 quick_help: quick_help},
100 101 common_options));
101 102 var notification_area = new notificationarea.NotificationArea(
102 103 '#notification_area', {
103 104 events: events,
104 105 save_widget: save_widget,
105 106 notebook: notebook,
106 107 keyboard_manager: keyboard_manager});
107 108 notification_area.init_notification_widgets();
108 109 var kernel_selector = new kernelselector.KernelSelector(
109 110 '#kernel_selector_widget', notebook);
110 111
111 112 $('body').append('<div id="fonttest"><pre><span id="test1">x</span>'+
112 113 '<span id="test2" style="font-weight: bold;">x</span>'+
113 114 '<span id="test3" style="font-style: italic;">x</span></pre></div>');
114 115 var nh = $('#test1').innerHeight();
115 116 var bh = $('#test2').innerHeight();
116 117 var ih = $('#test3').innerHeight();
117 118 if(nh != bh || nh != ih) {
118 119 $('head').append('<style>.CodeMirror span { vertical-align: bottom; }</style>');
119 120 }
120 121 $('#fonttest').remove();
121 122
122 123 page.show();
123 124
124 125 layout_manager.do_resize();
125 126 var first_load = function () {
126 127 layout_manager.do_resize();
127 128 var hash = document.location.hash;
128 129 if (hash) {
129 130 document.location.hash = '';
130 131 document.location.hash = hash;
131 132 }
132 133 notebook.set_autosave_interval(notebook.minimum_autosave_interval);
133 134 // only do this once
134 135 events.off('notebook_loaded.Notebook', first_load);
135 136 };
136 137 events.on('notebook_loaded.Notebook', first_load);
137 138
138 139 IPython.page = page;
139 140 IPython.layout_manager = layout_manager;
140 141 IPython.notebook = notebook;
141 142 IPython.contents = contents;
142 143 IPython.pager = pager;
143 144 IPython.quick_help = quick_help;
144 145 IPython.login_widget = login_widget;
145 146 IPython.menubar = menubar;
146 147 IPython.toolbar = toolbar;
147 148 IPython.notification_area = notification_area;
148 149 IPython.keyboard_manager = keyboard_manager;
149 150 IPython.save_widget = save_widget;
150 151 IPython.config = user_config;
151 152 IPython.tooltip = notebook.tooltip;
152 153
153 154 events.trigger('app_initialized.NotebookApp');
154 155 notebook.load_notebook(common_options.notebook_path);
155 156
156 157 });
@@ -1,194 +1,193
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 'base/js/namespace',
6 6 'jquery',
7 7 'base/js/utils',
8 'rsvp',
9 ], function(IPython, $, utils, rsvp) {
8 ], function(IPython, $, utils) {
10 9 "use strict";
11 10
12 11 //-----------------------------------------------------------------------
13 12 // CommManager class
14 13 //-----------------------------------------------------------------------
15 14
16 15 var CommManager = function (kernel) {
17 16 this.comms = {};
18 17 this.targets = {};
19 18 if (kernel !== undefined) {
20 19 this.init_kernel(kernel);
21 20 }
22 21 };
23 22
24 23 CommManager.prototype.init_kernel = function (kernel) {
25 24 // connect the kernel, and register message handlers
26 25 this.kernel = kernel;
27 26 var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
28 27 for (var i = 0; i < msg_types.length; i++) {
29 28 var msg_type = msg_types[i];
30 29 kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
31 30 }
32 31 };
33 32
34 33 CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
35 34 // Create a new Comm, register it, and open its Kernel-side counterpart
36 35 // Mimics the auto-registration in `Comm.__init__` in the IPython Comm
37 36 var comm = new Comm(target_name);
38 37 this.register_comm(comm);
39 38 comm.open(data, callbacks, metadata);
40 39 return comm;
41 40 };
42 41
43 42 CommManager.prototype.register_target = function (target_name, f) {
44 43 // Register a target function for a given target name
45 44 this.targets[target_name] = f;
46 45 };
47 46
48 47 CommManager.prototype.unregister_target = function (target_name, f) {
49 48 // Unregister a target function for a given target name
50 49 delete this.targets[target_name];
51 50 };
52 51
53 52 CommManager.prototype.register_comm = function (comm) {
54 53 // Register a comm in the mapping
55 54 this.comms[comm.comm_id] = comm;
56 55 comm.kernel = this.kernel;
57 56 return comm.comm_id;
58 57 };
59 58
60 59 CommManager.prototype.unregister_comm = function (comm) {
61 60 // Remove a comm from the mapping
62 61 delete this.comms[comm.comm_id];
63 62 };
64 63
65 64 // comm message handlers
66 65
67 66 CommManager.prototype.comm_open = function (msg) {
68 67 var content = msg.content;
69 68 var that = this;
70 69
71 70 return utils.load_class(content.target_name, content.target_module,
72 71 this.targets).then(function(target) {
73 72
74 73 var comm = new Comm(content.target_name, content.comm_id);
75 74 that.register_comm(comm);
76 75 try {
77 76 target(comm, msg);
78 77 } catch (e) {
79 78 comm.close();
80 79 that.unregister_comm(comm);
81 80 var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
82 81 console.error(wrapped_error);
83 return rsvp.Promise.reject(wrapped_error);
82 return Promise.reject(wrapped_error);
84 83 }
85 84 return comm;
86 85 }, utils.reject('Could not open comm', true));
87 86 };
88 87
89 88 CommManager.prototype.comm_close = function (msg) {
90 89 var content = msg.content;
91 90 var comm = this.comms[content.comm_id];
92 91 if (comm === undefined) {
93 92 return;
94 93 }
95 94 this.unregister_comm(comm);
96 95 try {
97 96 comm.handle_close(msg);
98 97 } catch (e) {
99 98 console.log("Exception closing comm: ", e, e.stack, msg);
100 99 }
101 100 };
102 101
103 102 CommManager.prototype.comm_msg = function (msg) {
104 103 var content = msg.content;
105 104 var comm = this.comms[content.comm_id];
106 105 if (comm === undefined) {
107 106 return;
108 107 }
109 108 try {
110 109 comm.handle_msg(msg);
111 110 } catch (e) {
112 111 console.log("Exception handling comm msg: ", e, e.stack, msg);
113 112 }
114 113 };
115 114
116 115 //-----------------------------------------------------------------------
117 116 // Comm base class
118 117 //-----------------------------------------------------------------------
119 118
120 119 var Comm = function (target_name, comm_id) {
121 120 this.target_name = target_name;
122 121 this.comm_id = comm_id || utils.uuid();
123 122 this._msg_callback = this._close_callback = null;
124 123 };
125 124
126 125 // methods for sending messages
127 126 Comm.prototype.open = function (data, callbacks, metadata) {
128 127 var content = {
129 128 comm_id : this.comm_id,
130 129 target_name : this.target_name,
131 130 data : data || {},
132 131 };
133 132 return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
134 133 };
135 134
136 135 Comm.prototype.send = function (data, callbacks, metadata, buffers) {
137 136 var content = {
138 137 comm_id : this.comm_id,
139 138 data : data || {},
140 139 };
141 140 return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
142 141 };
143 142
144 143 Comm.prototype.close = function (data, callbacks, metadata) {
145 144 var content = {
146 145 comm_id : this.comm_id,
147 146 data : data || {},
148 147 };
149 148 return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
150 149 };
151 150
152 151 // methods for registering callbacks for incoming messages
153 152 Comm.prototype._register_callback = function (key, callback) {
154 153 this['_' + key + '_callback'] = callback;
155 154 };
156 155
157 156 Comm.prototype.on_msg = function (callback) {
158 157 this._register_callback('msg', callback);
159 158 };
160 159
161 160 Comm.prototype.on_close = function (callback) {
162 161 this._register_callback('close', callback);
163 162 };
164 163
165 164 // methods for handling incoming messages
166 165
167 166 Comm.prototype._maybe_callback = function (key, msg) {
168 167 var callback = this['_' + key + '_callback'];
169 168 if (callback) {
170 169 try {
171 170 callback(msg);
172 171 } catch (e) {
173 172 console.log("Exception in Comm callback", e, e.stack, msg);
174 173 }
175 174 }
176 175 };
177 176
178 177 Comm.prototype.handle_msg = function (msg) {
179 178 this._maybe_callback('msg', msg);
180 179 };
181 180
182 181 Comm.prototype.handle_close = function (msg) {
183 182 this._maybe_callback('close', msg);
184 183 };
185 184
186 185 // For backwards compatability.
187 186 IPython.CommManager = CommManager;
188 187 IPython.Comm = Comm;
189 188
190 189 return {
191 190 'CommManager': CommManager,
192 191 'Comm': Comm
193 192 };
194 193 });
@@ -1,254 +1,253
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 "underscore",
6 6 "backbone",
7 7 "jquery",
8 8 "base/js/utils",
9 9 "base/js/namespace",
10 'rsvp',
11 ], function (_, Backbone, $, utils, IPython, rsvp) {
10 ], function (_, Backbone, $, utils, IPython) {
12 11 "use strict";
13 12 //--------------------------------------------------------------------
14 13 // WidgetManager class
15 14 //--------------------------------------------------------------------
16 15 var WidgetManager = function (comm_manager, notebook) {
17 16 // Public constructor
18 17 WidgetManager._managers.push(this);
19 18
20 19 // Attach a comm manager to the
21 20 this.keyboard_manager = notebook.keyboard_manager;
22 21 this.notebook = notebook;
23 22 this.comm_manager = comm_manager;
24 23 this._models = {}; /* Dictionary of model ids and model instances */
25 24
26 25 // Register with the comm manager.
27 26 this.comm_manager.register_target('ipython.widget', $.proxy(this._handle_comm_open, this));
28 27 };
29 28
30 29 //--------------------------------------------------------------------
31 30 // Class level
32 31 //--------------------------------------------------------------------
33 32 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
34 33 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
35 34 WidgetManager._managers = []; /* List of widget managers */
36 35
37 36 WidgetManager.register_widget_model = function (model_name, model_type) {
38 37 // Registers a widget model by name.
39 38 WidgetManager._model_types[model_name] = model_type;
40 39 };
41 40
42 41 WidgetManager.register_widget_view = function (view_name, view_type) {
43 42 // Registers a widget view by name.
44 43 WidgetManager._view_types[view_name] = view_type;
45 44 };
46 45
47 46 //--------------------------------------------------------------------
48 47 // Instance level
49 48 //--------------------------------------------------------------------
50 49 WidgetManager.prototype.display_view = function(msg, model) {
51 50 // Displays a view for a particular model.
52 51 var that = this;
53 return new rsvp.Promise(function(resolve, reject) {
52 return new Promise(function(resolve, reject) {
54 53 var cell = that.get_msg_cell(msg.parent_header.msg_id);
55 54 if (cell === null) {
56 55 reject(new Error("Could not determine where the display" +
57 56 " message was from. Widget will not be displayed"));
58 57 } else {
59 58 var dummy = null;
60 59 if (cell.widget_subarea) {
61 60 dummy = $('<div />');
62 61 cell.widget_subarea.append(dummy);
63 62 }
64 63
65 64 that.create_view(model, {cell: cell}).then(function(view) {
66 65 that._handle_display_view(view);
67 66 if (dummy) {
68 67 dummy.replaceWith(view.$el);
69 68 }
70 69 view.trigger('displayed');
71 70 resolve(view);
72 71 }, function(error) {
73 72 reject(new utils.WrappedError('Could not display view', error));
74 73 });
75 74 }
76 75 });
77 76 };
78 77
79 78 WidgetManager.prototype._handle_display_view = function (view) {
80 79 // Have the IPython keyboard manager disable its event
81 80 // handling so the widget can capture keyboard input.
82 81 // Note, this is only done on the outer most widgets.
83 82 if (this.keyboard_manager) {
84 83 this.keyboard_manager.register_events(view.$el);
85 84
86 85 if (view.additional_elements) {
87 86 for (var i = 0; i < view.additional_elements.length; i++) {
88 87 this.keyboard_manager.register_events(view.additional_elements[i]);
89 88 }
90 89 }
91 90 }
92 91 };
93 92
94 93 WidgetManager.prototype.create_view = function(model, options) {
95 94 // Creates a promise for a view of a given model
96 95
97 96 // Make sure the view creation is not out of order with
98 97 // any state updates.
99 98 model.state_change = model.state_change.then(function() {
100 99 try {
101 100 console.log('create_view ' + model.id);
102 101 console.log(' _view_name ' + model.get('_view_name'));
103 102 console.log(' _view_module ' + model.get('_view_module'));
104 103 } catch (e) { }
105 104
106 105 return utils.load_class(model.get('_view_name'), model.get('_view_module'),
107 106 WidgetManager._view_types).then(function(ViewType) {
108 107
109 108 // If a view is passed into the method, use that view's cell as
110 109 // the cell for the view that is created.
111 110 options = options || {};
112 111 if (options.parent !== undefined) {
113 112 options.cell = options.parent.options.cell;
114 113 }
115 114 // Create and render the view...
116 115 var parameters = {model: model, options: options};
117 116 var view = new ViewType(parameters);
118 117 view.listenTo(model, 'destroy', view.remove);
119 118 view.render();
120 119 return view;
121 120 }, utils.reject("Couldn't create a view for model id '" + String(model.id) + "'"));
122 121 });
123 122 return model.state_change;
124 123 };
125 124
126 125 WidgetManager.prototype.get_msg_cell = function (msg_id) {
127 126 var cell = null;
128 127 // First, check to see if the msg was triggered by cell execution.
129 128 if (this.notebook) {
130 129 cell = this.notebook.get_msg_cell(msg_id);
131 130 }
132 131 if (cell !== null) {
133 132 return cell;
134 133 }
135 134 // Second, check to see if a get_cell callback was defined
136 135 // for the message. get_cell callbacks are registered for
137 136 // widget messages, so this block is actually checking to see if the
138 137 // message was triggered by a widget.
139 138 var kernel = this.comm_manager.kernel;
140 139 if (kernel) {
141 140 var callbacks = kernel.get_callbacks_for_msg(msg_id);
142 141 if (callbacks && callbacks.iopub &&
143 142 callbacks.iopub.get_cell !== undefined) {
144 143 return callbacks.iopub.get_cell();
145 144 }
146 145 }
147 146
148 147 // Not triggered by a cell or widget (no get_cell callback
149 148 // exists).
150 149 return null;
151 150 };
152 151
153 152 WidgetManager.prototype.callbacks = function (view) {
154 153 // callback handlers specific a view
155 154 var callbacks = {};
156 155 if (view && view.options.cell) {
157 156
158 157 // Try to get output handlers
159 158 var cell = view.options.cell;
160 159 var handle_output = null;
161 160 var handle_clear_output = null;
162 161 if (cell.output_area) {
163 162 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
164 163 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
165 164 }
166 165
167 166 // Create callback dictionary using what is known
168 167 var that = this;
169 168 callbacks = {
170 169 iopub : {
171 170 output : handle_output,
172 171 clear_output : handle_clear_output,
173 172
174 173 // Special function only registered by widget messages.
175 174 // Allows us to get the cell for a message so we know
176 175 // where to add widgets if the code requires it.
177 176 get_cell : function () {
178 177 return cell;
179 178 },
180 179 },
181 180 };
182 181 }
183 182 return callbacks;
184 183 };
185 184
186 185 WidgetManager.prototype.get_model = function (model_id) {
187 186 return this._models[model_id];
188 187 };
189 188
190 189 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
191 190 // Handle when a comm is opened.
192 191 this.create_model({
193 192 model_name: msg.content.data.model_name,
194 193 model_module: msg.content.data.model_module,
195 194 comm: comm}).catch($.proxy(console.error, console));
196 195 };
197 196
198 197 WidgetManager.prototype.create_model = function (options) {
199 198 // Create and return a promise for a new widget model
200 199 //
201 200 // Minimally, one must provide the model_name and widget_class
202 201 // parameters to create a model from Javascript.
203 202 //
204 203 // Example
205 204 // --------
206 205 // JS:
207 206 // IPython.notebook.kernel.widget_manager.create_model({
208 207 // model_name: 'WidgetModel',
209 208 // widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
210 209 // .then(function(model) { console.log('Create success!', model); },
211 210 // $.proxy(console.error, console));
212 211 //
213 212 // Parameters
214 213 // ----------
215 214 // options: dictionary
216 215 // Dictionary of options with the following contents:
217 216 // model_name: string
218 217 // Target name of the widget model to create.
219 218 // model_module: (optional) string
220 219 // Module name of the widget model to create.
221 220 // widget_class: (optional) string
222 221 // Target name of the widget in the back-end.
223 222 // comm: (optional) Comm
224 223
225 224 // Create a comm if it wasn't provided.
226 225 var comm = options.comm;
227 226 if (!comm) {
228 227 comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
229 228 }
230 229
231 230 var that = this;
232 231 var model_id = comm.comm_id;
233 232 var model_promise = utils.load_class(options.model_name, options.model_module, WidgetManager._model_types)
234 233 .then(function(ModelType) {
235 234 var widget_model = new ModelType(that, model_id, comm);
236 235 widget_model.once('comm:close', function () {
237 236 delete that._models[model_id];
238 237 });
239 238 return widget_model;
240 239
241 240 }, function(error) {
242 241 delete that._models[model_id];
243 242 var wrapped_error = new utils.WrappedError("Couldn't create model", error);
244 return rsvp.Promise.reject(wrapped_error);
243 return Promise.reject(wrapped_error);
245 244 });
246 245 this._models[model_id] = model_promise;
247 246 return model_promise;
248 247 };
249 248
250 249 // Backwards compatibility.
251 250 IPython.WidgetManager = WidgetManager;
252 251
253 252 return {'WidgetManager': WidgetManager};
254 253 });
@@ -1,609 +1,608
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define(["widgets/js/manager",
5 5 "underscore",
6 6 "backbone",
7 7 "jquery",
8 8 "base/js/utils",
9 9 "base/js/namespace",
10 "rsvp",
11 ], function(widgetmanager, _, Backbone, $, utils, IPython, rsvp){
10 ], function(widgetmanager, _, Backbone, $, utils, IPython){
12 11
13 12 var WidgetModel = Backbone.Model.extend({
14 13 constructor: function (widget_manager, model_id, comm) {
15 14 // Constructor
16 15 //
17 16 // Creates a WidgetModel instance.
18 17 //
19 18 // Parameters
20 19 // ----------
21 20 // widget_manager : WidgetManager instance
22 21 // model_id : string
23 22 // An ID unique to this model.
24 23 // comm : Comm instance (optional)
25 24 this.widget_manager = widget_manager;
26 this.state_change = rsvp.Promise.resolve();
25 this.state_change = Promise.resolve();
27 26 this._buffered_state_diff = {};
28 27 this.pending_msgs = 0;
29 28 this.msg_buffer = null;
30 29 this.state_lock = null;
31 30 this.id = model_id;
32 31 this.views = {};
33 32
34 33 if (comm !== undefined) {
35 34 // Remember comm associated with the model.
36 35 this.comm = comm;
37 36 comm.model = this;
38 37
39 38 // Hook comm messages up to model.
40 39 comm.on_close($.proxy(this._handle_comm_closed, this));
41 40 comm.on_msg($.proxy(this._handle_comm_msg, this));
42 41 }
43 42 return Backbone.Model.apply(this);
44 43 },
45 44
46 45 send: function (content, callbacks) {
47 46 // Send a custom msg over the comm.
48 47 if (this.comm !== undefined) {
49 48 var data = {method: 'custom', content: content};
50 49 this.comm.send(data, callbacks);
51 50 this.pending_msgs++;
52 51 }
53 52 },
54 53
55 54 _handle_comm_closed: function (msg) {
56 55 // Handle when a widget is closed.
57 56 this.trigger('comm:close');
58 57 this.stopListening();
59 58 this.trigger('destroy', this);
60 59 delete this.comm.model; // Delete ref so GC will collect widget model.
61 60 delete this.comm;
62 61 delete this.model_id; // Delete id from model so widget manager cleans up.
63 62 for (var id in this.views) {
64 63 if (this.views.hasOwnProperty(id)) {
65 64 this.views[id].remove();
66 65 }
67 66 }
68 67 },
69 68
70 69 _handle_comm_msg: function (msg) {
71 70 // Handle incoming comm msg.
72 71 var method = msg.content.data.method;
73 72 console.log(method);
74 73 var that = this;
75 74 switch (method) {
76 75 case 'update':
77 76 this.state_change = this.state_change.then(function() {
78 77 return that.set_state(msg.content.data.state);
79 78 }).catch(utils.reject("Couldn't process update msg for model id '" + String(that.id) + "'", true));
80 79 break;
81 80 case 'custom':
82 81 this.trigger('msg:custom', msg.content.data.content);
83 82 break;
84 83 case 'display':
85 84 this.state_change = this.state_change.then(function() {
86 85 return that.widget_manager.display_view(msg, that);
87 86 }).catch(utils.reject("Couldn't process display msg for model id '" + String(that.id) + "'", true));
88 87 break;
89 88 }
90 89 },
91 90
92 91 set_state: function (state) {
93 92 var that = this;
94 93 // Handle when a widget is updated via the python side.
95 94 return this._unpack_models(state).then(function(state) {
96 95 that.state_lock = state;
97 96 try {
98 97 console.log('set_state ' + that.id);
99 98 console.log(state);
100 99 WidgetModel.__super__.set.call(that, state);
101 100 } finally {
102 101 that.state_lock = null;
103 102 }
104 return rsvp.Promise.resolve();
103 return Promise.resolve();
105 104 }, utils.reject("Couldn't set model state", true));
106 105 },
107 106
108 107 _handle_status: function (msg, callbacks) {
109 108 // Handle status msgs.
110 109
111 110 // execution_state : ('busy', 'idle', 'starting')
112 111 if (this.comm !== undefined) {
113 112 if (msg.content.execution_state ==='idle') {
114 113 // Send buffer if this message caused another message to be
115 114 // throttled.
116 115 if (this.msg_buffer !== null &&
117 116 (this.get('msg_throttle') || 3) === this.pending_msgs) {
118 117 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
119 118 this.comm.send(data, callbacks);
120 119 this.msg_buffer = null;
121 120 } else {
122 121 --this.pending_msgs;
123 122 }
124 123 }
125 124 }
126 125 },
127 126
128 127 callbacks: function(view) {
129 128 // Create msg callbacks for a comm msg.
130 129 var callbacks = this.widget_manager.callbacks(view);
131 130
132 131 if (callbacks.iopub === undefined) {
133 132 callbacks.iopub = {};
134 133 }
135 134
136 135 var that = this;
137 136 callbacks.iopub.status = function (msg) {
138 137 that._handle_status(msg, callbacks);
139 138 };
140 139 return callbacks;
141 140 },
142 141
143 142 set: function(key, val, options) {
144 143 // Set a value.
145 144 var return_value = WidgetModel.__super__.set.apply(this, arguments);
146 145
147 146 // Backbone only remembers the diff of the most recent set()
148 147 // operation. Calling set multiple times in a row results in a
149 148 // loss of diff information. Here we keep our own running diff.
150 149 this._buffered_state_diff = $.extend(this._buffered_state_diff, this.changedAttributes() || {});
151 150 return return_value;
152 151 },
153 152
154 153 sync: function (method, model, options) {
155 154 // Handle sync to the back-end. Called when a model.save() is called.
156 155
157 156 // Make sure a comm exists.
158 157 var error = options.error || function() {
159 158 console.error('Backbone sync error:', arguments);
160 159 };
161 160 if (this.comm === undefined) {
162 161 error();
163 162 return false;
164 163 }
165 164
166 165 // Delete any key value pairs that the back-end already knows about.
167 166 var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
168 167 if (this.state_lock !== null) {
169 168 var keys = Object.keys(this.state_lock);
170 169 for (var i=0; i<keys.length; i++) {
171 170 var key = keys[i];
172 171 if (attrs[key] === this.state_lock[key]) {
173 172 delete attrs[key];
174 173 }
175 174 }
176 175 }
177 176
178 177 // Only sync if there are attributes to send to the back-end.
179 178 attrs = this._pack_models(attrs);
180 179 if (_.size(attrs) > 0) {
181 180
182 181 // If this message was sent via backbone itself, it will not
183 182 // have any callbacks. It's important that we create callbacks
184 183 // so we can listen for status messages, etc...
185 184 var callbacks = options.callbacks || this.callbacks();
186 185
187 186 // Check throttle.
188 187 if (this.pending_msgs >= (this.get('msg_throttle') || 3)) {
189 188 // The throttle has been exceeded, buffer the current msg so
190 189 // it can be sent once the kernel has finished processing
191 190 // some of the existing messages.
192 191
193 192 // Combine updates if it is a 'patch' sync, otherwise replace updates
194 193 switch (method) {
195 194 case 'patch':
196 195 this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
197 196 break;
198 197 case 'update':
199 198 case 'create':
200 199 this.msg_buffer = attrs;
201 200 break;
202 201 default:
203 202 error();
204 203 return false;
205 204 }
206 205 this.msg_buffer_callbacks = callbacks;
207 206
208 207 } else {
209 208 // We haven't exceeded the throttle, send the message like
210 209 // normal.
211 210 var data = {method: 'backbone', sync_data: attrs};
212 211 this.comm.send(data, callbacks);
213 212 this.pending_msgs++;
214 213 }
215 214 }
216 215 // Since the comm is a one-way communication, assume the message
217 216 // arrived. Don't call success since we don't have a model back from the server
218 217 // this means we miss out on the 'sync' event.
219 218 this._buffered_state_diff = {};
220 219 },
221 220
222 221 save_changes: function(callbacks) {
223 222 // Push this model's state to the back-end
224 223 //
225 224 // This invokes a Backbone.Sync.
226 225 this.save(this._buffered_state_diff, {patch: true, callbacks: callbacks});
227 226 },
228 227
229 228 _pack_models: function(value) {
230 229 // Replace models with model ids recursively.
231 230 var that = this;
232 231 var packed;
233 232 if (value instanceof Backbone.Model) {
234 233 return "IPY_MODEL_" + value.id;
235 234
236 235 } else if ($.isArray(value)) {
237 236 packed = [];
238 237 _.each(value, function(sub_value, key) {
239 238 packed.push(that._pack_models(sub_value));
240 239 });
241 240 return packed;
242 241 } else if (value instanceof Date || value instanceof String) {
243 242 return value;
244 243 } else if (value instanceof Object) {
245 244 packed = {};
246 245 _.each(value, function(sub_value, key) {
247 246 packed[key] = that._pack_models(sub_value);
248 247 });
249 248 return packed;
250 249
251 250 } else {
252 251 return value;
253 252 }
254 253 },
255 254
256 255 _unpack_models: function(value) {
257 256 // Replace model ids with models recursively.
258 257 var that = this;
259 258 var unpacked;
260 259 if ($.isArray(value)) {
261 260 unpacked = [];
262 261 _.each(value, function(sub_value, key) {
263 262 unpacked.push(that._unpack_models(sub_value));
264 263 });
265 return rsvp.Promise.all(unpacked);
264 return Promise.all(unpacked);
266 265 } else if (value instanceof Object) {
267 266 unpacked = {};
268 267 _.each(value, function(sub_value, key) {
269 268 unpacked[key] = that._unpack_models(sub_value);
270 269 });
271 270 return utils.resolve_dict(unpacked);
272 271 } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") {
273 272 // get_model returns a promise already
274 273 return this.widget_manager.get_model(value.slice(10, value.length));
275 274 } else {
276 return rsvp.Promise.resolve(value);
275 return Promise.resolve(value);
277 276 }
278 277 },
279 278
280 279 on_some_change: function(keys, callback, context) {
281 280 // on_some_change(["key1", "key2"], foo, context) differs from
282 281 // on("change:key1 change:key2", foo, context).
283 282 // If the widget attributes key1 and key2 are both modified,
284 283 // the second form will result in foo being called twice
285 284 // while the first will call foo only once.
286 285 this.on('change', function() {
287 286 if (keys.some(this.hasChanged, this)) {
288 287 callback.apply(context);
289 288 }
290 289 }, this);
291 290
292 291 },
293 292 });
294 293 widgetmanager.WidgetManager.register_widget_model('WidgetModel', WidgetModel);
295 294
296 295
297 296 var WidgetView = Backbone.View.extend({
298 297 initialize: function(parameters) {
299 298 // Public constructor.
300 299 this.model.on('change',this.update,this);
301 300 this.options = parameters.options;
302 301 this.child_model_views = {};
303 302 this.child_views = {};
304 303 this.id = this.id || utils.uuid();
305 304 this.model.views[this.id] = this;
306 305 this.on('displayed', function() {
307 306 this.is_displayed = true;
308 307 }, this);
309 308 },
310 309
311 310 update: function(){
312 311 // Triggered on model change.
313 312 //
314 313 // Update view to be consistent with this.model
315 314 },
316 315
317 316 create_child_view: function(child_model, options) {
318 317 // Create and promise that resolves to a child view of a given model
319 318 var that = this;
320 319 options = $.extend({ parent: this }, options || {});
321 320 return this.model.widget_manager.create_view(child_model, options).then(function(child_view) {
322 321 // Associate the view id with the model id.
323 322 if (that.child_model_views[child_model.id] === undefined) {
324 323 that.child_model_views[child_model.id] = [];
325 324 }
326 325 that.child_model_views[child_model.id].push(child_view.id);
327 326 // Remember the view by id.
328 327 that.child_views[child_view.id] = child_view;
329 328 return child_view;
330 329 }, utils.reject("Couldn't create child view"));
331 330 },
332 331
333 332 pop_child_view: function(child_model) {
334 333 // Delete a child view that was previously created using create_child_view.
335 334 var view_ids = this.child_model_views[child_model.id];
336 335 if (view_ids !== undefined) {
337 336
338 337 // Only delete the first view in the list.
339 338 var view_id = view_ids[0];
340 339 var view = this.child_views[view_id];
341 340 delete this.child_views[view_id];
342 341 view_ids.splice(0,1);
343 342 delete child_model.views[view_id];
344 343
345 344 // Remove the view list specific to this model if it is empty.
346 345 if (view_ids.length === 0) {
347 346 delete this.child_model_views[child_model.id];
348 347 }
349 348 return view;
350 349 }
351 350 return null;
352 351 },
353 352
354 353 do_diff: function(old_list, new_list, removed_callback, added_callback) {
355 354 // Difference a changed list and call remove and add callbacks for
356 355 // each removed and added item in the new list.
357 356 //
358 357 // Parameters
359 358 // ----------
360 359 // old_list : array
361 360 // new_list : array
362 361 // removed_callback : Callback(item)
363 362 // Callback that is called for each item removed.
364 363 // added_callback : Callback(item)
365 364 // Callback that is called for each item added.
366 365
367 366 // Walk the lists until an unequal entry is found.
368 367 var i;
369 368 for (i = 0; i < new_list.length; i++) {
370 369 if (i >= old_list.length || new_list[i] !== old_list[i]) {
371 370 break;
372 371 }
373 372 }
374 373
375 374 // Remove the non-matching items from the old list.
376 375 for (var j = i; j < old_list.length; j++) {
377 376 removed_callback(old_list[j]);
378 377 }
379 378
380 379 // Add the rest of the new list items.
381 380 for (; i < new_list.length; i++) {
382 381 added_callback(new_list[i]);
383 382 }
384 383 },
385 384
386 385 callbacks: function(){
387 386 // Create msg callbacks for a comm msg.
388 387 return this.model.callbacks(this);
389 388 },
390 389
391 390 render: function(){
392 391 // Render the view.
393 392 //
394 393 // By default, this is only called the first time the view is created
395 394 },
396 395
397 396 show: function(){
398 397 // Show the widget-area
399 398 if (this.options && this.options.cell &&
400 399 this.options.cell.widget_area !== undefined) {
401 400 this.options.cell.widget_area.show();
402 401 }
403 402 },
404 403
405 404 send: function (content) {
406 405 // Send a custom msg associated with this view.
407 406 this.model.send(content, this.callbacks());
408 407 },
409 408
410 409 touch: function () {
411 410 this.model.save_changes(this.callbacks());
412 411 },
413 412
414 413 after_displayed: function (callback, context) {
415 414 // Calls the callback right away is the view is already displayed
416 415 // otherwise, register the callback to the 'displayed' event.
417 416 if (this.is_displayed) {
418 417 callback.apply(context);
419 418 } else {
420 419 this.on('displayed', callback, context);
421 420 }
422 421 },
423 422 });
424 423
425 424
426 425 var DOMWidgetView = WidgetView.extend({
427 426 initialize: function (parameters) {
428 427 // Public constructor
429 428 DOMWidgetView.__super__.initialize.apply(this, [parameters]);
430 429 this.on('displayed', this.show, this);
431 430 this.model.on('change:visible', this.update_visible, this);
432 431 this.model.on('change:_css', this.update_css, this);
433 432
434 433 this.model.on('change:_dom_classes', function(model, new_classes) {
435 434 var old_classes = model.previous('_dom_classes');
436 435 this.update_classes(old_classes, new_classes);
437 436 }, this);
438 437
439 438 this.model.on('change:color', function (model, value) {
440 439 this.update_attr('color', value); }, this);
441 440
442 441 this.model.on('change:background_color', function (model, value) {
443 442 this.update_attr('background', value); }, this);
444 443
445 444 this.model.on('change:width', function (model, value) {
446 445 this.update_attr('width', value); }, this);
447 446
448 447 this.model.on('change:height', function (model, value) {
449 448 this.update_attr('height', value); }, this);
450 449
451 450 this.model.on('change:border_color', function (model, value) {
452 451 this.update_attr('border-color', value); }, this);
453 452
454 453 this.model.on('change:border_width', function (model, value) {
455 454 this.update_attr('border-width', value); }, this);
456 455
457 456 this.model.on('change:border_style', function (model, value) {
458 457 this.update_attr('border-style', value); }, this);
459 458
460 459 this.model.on('change:font_style', function (model, value) {
461 460 this.update_attr('font-style', value); }, this);
462 461
463 462 this.model.on('change:font_weight', function (model, value) {
464 463 this.update_attr('font-weight', value); }, this);
465 464
466 465 this.model.on('change:font_size', function (model, value) {
467 466 this.update_attr('font-size', this._default_px(value)); }, this);
468 467
469 468 this.model.on('change:font_family', function (model, value) {
470 469 this.update_attr('font-family', value); }, this);
471 470
472 471 this.model.on('change:padding', function (model, value) {
473 472 this.update_attr('padding', value); }, this);
474 473
475 474 this.model.on('change:margin', function (model, value) {
476 475 this.update_attr('margin', this._default_px(value)); }, this);
477 476
478 477 this.model.on('change:border_radius', function (model, value) {
479 478 this.update_attr('border-radius', this._default_px(value)); }, this);
480 479
481 480 this.after_displayed(function() {
482 481 this.update_visible(this.model, this.model.get("visible"));
483 482 this.update_classes([], this.model.get('_dom_classes'));
484 483
485 484 this.update_attr('color', this.model.get('color'));
486 485 this.update_attr('background', this.model.get('background_color'));
487 486 this.update_attr('width', this.model.get('width'));
488 487 this.update_attr('height', this.model.get('height'));
489 488 this.update_attr('border-color', this.model.get('border_color'));
490 489 this.update_attr('border-width', this.model.get('border_width'));
491 490 this.update_attr('border-style', this.model.get('border_style'));
492 491 this.update_attr('font-style', this.model.get('font_style'));
493 492 this.update_attr('font-weight', this.model.get('font_weight'));
494 493 this.update_attr('font-size', this.model.get('font_size'));
495 494 this.update_attr('font-family', this.model.get('font_family'));
496 495 this.update_attr('padding', this.model.get('padding'));
497 496 this.update_attr('margin', this.model.get('margin'));
498 497 this.update_attr('border-radius', this.model.get('border_radius'));
499 498
500 499 this.update_css(this.model, this.model.get("_css"));
501 500 }, this);
502 501 },
503 502
504 503 _default_px: function(value) {
505 504 // Makes browser interpret a numerical string as a pixel value.
506 505 if (/^\d+\.?(\d+)?$/.test(value.trim())) {
507 506 return value.trim() + 'px';
508 507 }
509 508 return value;
510 509 },
511 510
512 511 update_attr: function(name, value) {
513 512 // Set a css attr of the widget view.
514 513 this.$el.css(name, value);
515 514 },
516 515
517 516 update_visible: function(model, value) {
518 517 // Update visibility
519 518 this.$el.toggle(value);
520 519 },
521 520
522 521 update_css: function (model, css) {
523 522 // Update the css styling of this view.
524 523 var e = this.$el;
525 524 if (css === undefined) {return;}
526 525 for (var i = 0; i < css.length; i++) {
527 526 // Apply the css traits to all elements that match the selector.
528 527 var selector = css[i][0];
529 528 var elements = this._get_selector_element(selector);
530 529 if (elements.length > 0) {
531 530 var trait_key = css[i][1];
532 531 var trait_value = css[i][2];
533 532 elements.css(trait_key ,trait_value);
534 533 }
535 534 }
536 535 },
537 536
538 537 update_classes: function (old_classes, new_classes, $el) {
539 538 // Update the DOM classes applied to an element, default to this.$el.
540 539 if ($el===undefined) {
541 540 $el = this.$el;
542 541 }
543 542 this.do_diff(old_classes, new_classes, function(removed) {
544 543 $el.removeClass(removed);
545 544 }, function(added) {
546 545 $el.addClass(added);
547 546 });
548 547 },
549 548
550 549 update_mapped_classes: function(class_map, trait_name, previous_trait_value, $el) {
551 550 // Update the DOM classes applied to the widget based on a single
552 551 // trait's value.
553 552 //
554 553 // Given a trait value classes map, this function automatically
555 554 // handles applying the appropriate classes to the widget element
556 555 // and removing classes that are no longer valid.
557 556 //
558 557 // Parameters
559 558 // ----------
560 559 // class_map: dictionary
561 560 // Dictionary of trait values to class lists.
562 561 // Example:
563 562 // {
564 563 // success: ['alert', 'alert-success'],
565 564 // info: ['alert', 'alert-info'],
566 565 // warning: ['alert', 'alert-warning'],
567 566 // danger: ['alert', 'alert-danger']
568 567 // };
569 568 // trait_name: string
570 569 // Name of the trait to check the value of.
571 570 // previous_trait_value: optional string, default ''
572 571 // Last trait value
573 572 // $el: optional jQuery element handle, defaults to this.$el
574 573 // Element that the classes are applied to.
575 574 var key = previous_trait_value;
576 575 if (key === undefined) {
577 576 key = this.model.previous(trait_name);
578 577 }
579 578 var old_classes = class_map[key] ? class_map[key] : [];
580 579 key = this.model.get(trait_name);
581 580 var new_classes = class_map[key] ? class_map[key] : [];
582 581
583 582 this.update_classes(old_classes, new_classes, $el || this.$el);
584 583 },
585 584
586 585 _get_selector_element: function (selector) {
587 586 // Get the elements via the css selector.
588 587 var elements;
589 588 if (!selector) {
590 589 elements = this.$el;
591 590 } else {
592 591 elements = this.$el.find(selector).addBack(selector);
593 592 }
594 593 return elements;
595 594 },
596 595 });
597 596
598 597
599 598 var widget = {
600 599 'WidgetModel': WidgetModel,
601 600 'WidgetView': WidgetView,
602 601 'DOMWidgetView': DOMWidgetView,
603 602 };
604 603
605 604 // For backwards compatability.
606 605 $.extend(IPython, widget);
607 606
608 607 return widget;
609 608 });
@@ -1,105 +1,104
1 1 <!DOCTYPE HTML>
2 2 <html>
3 3
4 4 <head>
5 5 <meta charset="utf-8">
6 6
7 7 <title>{% block title %}IPython Notebook{% endblock %}</title>
8 8 <link rel="shortcut icon" type="image/x-icon" href="{{static_url("base/images/favicon.ico") }}">
9 9 <meta http-equiv="X-UA-Compatible" content="chrome=1">
10 10 <link rel="stylesheet" href="{{static_url("components/jquery-ui/themes/smoothness/jquery-ui.min.css") }}" type="text/css" />
11 11 <meta name="viewport" content="width=device-width, initial-scale=1.0">
12 12
13 13 {% block stylesheet %}
14 14 <link rel="stylesheet" href="{{ static_url("style/style.min.css") }}" type="text/css"/>
15 15 {% endblock %}
16 16 <link rel="stylesheet" href="{{ static_url("custom/custom.css") }}" type="text/css" />
17 17 <script src="{{static_url("components/es6-promise/promise.min.js")}}" type="text/javascript" charset="utf-8"></script>
18 18 <script src="{{static_url("components/requirejs/require.js") }}" type="text/javascript" charset="utf-8"></script>
19 19 <script>
20 20 require.config({
21 21 baseUrl: '{{static_url("", include_version=False)}}',
22 22 paths: {
23 23 nbextensions : '{{ base_url }}nbextensions',
24 24 underscore : 'components/underscore/underscore-min',
25 25 backbone : 'components/backbone/backbone-min',
26 26 jquery: 'components/jquery/jquery.min',
27 27 bootstrap: 'components/bootstrap/js/bootstrap.min',
28 28 bootstraptour: 'components/bootstrap-tour/build/js/bootstrap-tour.min',
29 29 jqueryui: 'components/jquery-ui/ui/minified/jquery-ui.min',
30 30 moment: "components/moment/moment",
31 31 codemirror: 'components/codemirror',
32 32 termjs: "components/term.js/src/term",
33 33 contents: '{{ contents_js_source }}',
34 rsvp: "components/rsvp/rsvp",
35 34 },
36 35 shim: {
37 36 underscore: {
38 37 exports: '_'
39 38 },
40 39 backbone: {
41 40 deps: ["underscore", "jquery"],
42 41 exports: "Backbone"
43 42 },
44 43 bootstrap: {
45 44 deps: ["jquery"],
46 45 exports: "bootstrap"
47 46 },
48 47 bootstraptour: {
49 48 deps: ["bootstrap"],
50 49 exports: "Tour"
51 50 },
52 51 jqueryui: {
53 52 deps: ["jquery"],
54 53 exports: "$"
55 54 }
56 55 }
57 56 });
58 57 </script>
59 58
60 59 {% block meta %}
61 60 {% endblock %}
62 61
63 62 </head>
64 63
65 64 <body {% block params %}{% endblock %}>
66 65
67 66 <noscript>
68 67 <div id='noscript'>
69 68 IPython Notebook requires JavaScript.<br>
70 69 Please enable it to proceed.
71 70 </div>
72 71 </noscript>
73 72
74 73 <div id="header" class="navbar navbar-static-top">
75 74 <div class="container">
76 75 <div id="ipython_notebook" class="nav navbar-brand pull-left"><a href="{{base_url}}tree" alt='dashboard'><img src='{{static_url("base/images/ipynblogo.png") }}' alt='IPython Notebook'/></a></div>
77 76
78 77 {% block login_widget %}
79 78
80 79 <span id="login_widget">
81 80 {% if logged_in %}
82 81 <button id="logout">Logout</button>
83 82 {% elif login_available and not logged_in %}
84 83 <button id="login">Login</button>
85 84 {% endif %}
86 85 </span>
87 86
88 87 {% endblock %}
89 88
90 89 {% block header %}
91 90 {% endblock %}
92 91 </div>
93 92 </div>
94 93
95 94 <div id="site">
96 95 {% block site %}
97 96 {% endblock %}
98 97 </div>
99 98
100 99 {% block script %}
101 100 {% endblock %}
102 101
103 102 </body>
104 103
105 104 </html>
General Comments 0
You need to be logged in to leave comments. Login now