##// END OF EJS Templates
Adding security.js with 1st attempt at is_safe.
Brian E. Granger -
Show More
@@ -0,0 +1,52 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2014 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // Utilities
10 //============================================================================
11 IPython.namespace('IPython.security');
12
13 IPython.security = (function (IPython) {
14 "use strict";
15
16 var utils = IPython.utils;
17
18 var is_safe = function (html) {
19 // Is the html string safe against JavaScript based attacks. This
20 // detects 1) black listed tags, 2) blacklisted attributes, 3) all
21 // event attributes (onhover, onclick, etc.).
22 var black_tags = ['script', 'style'];
23 var black_attrs = ['style'];
24 var wrapped_html = '<div>'+html+'</div>';
25 var e = $(wrapped_html);
26 var safe = true;
27 // Detect black listed tags
28 $.map(black_tags, function (tag, index) {
29 if (e.find(tag).length > 0) {
30 safe = false;
31 }
32 });
33 // Detect black listed attributes
34 $.map(black_attrs, function (attr, index) {
35 if (e.find('['+attr+']').length > 0) {
36 safe = false;
37 }
38 });
39 e.find('*').each(function (index) {
40 $.map(utils.get_attr_names($(this)), function (attr, index) {
41 if (attr.match('^on')) {safe = false;}
42 });
43 })
44 return safe;
45 }
46
47 return {
48 is_safe: is_safe
49 };
50
51 }(IPython));
52
@@ -1,514 +1,524 b''
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 IPython.load_extensions = function () {
17 17 // load one or more IPython notebook extensions with requirejs
18 18
19 19 var extensions = [];
20 20 var extension_names = arguments;
21 21 for (var i = 0; i < extension_names.length; i++) {
22 22 extensions.push("nbextensions/" + arguments[i]);
23 23 }
24 24
25 25 require(extensions,
26 26 function () {
27 27 for (var i = 0; i < arguments.length; i++) {
28 28 var ext = arguments[i];
29 29 var ext_name = extension_names[i];
30 30 // success callback
31 31 console.log("Loaded extension: " + ext_name);
32 32 if (ext && ext.load_ipython_extension !== undefined) {
33 33 ext.load_ipython_extension();
34 34 }
35 35 }
36 36 },
37 37 function (err) {
38 38 // failure callback
39 39 console.log("Failed to load extension(s):", err.requireModules, err);
40 40 }
41 41 );
42 42 };
43 43
44 44 //============================================================================
45 45 // Cross-browser RegEx Split
46 46 //============================================================================
47 47
48 48 // This code has been MODIFIED from the code licensed below to not replace the
49 49 // default browser split. The license is reproduced here.
50 50
51 51 // see http://blog.stevenlevithan.com/archives/cross-browser-split for more info:
52 52 /*!
53 53 * Cross-Browser Split 1.1.1
54 54 * Copyright 2007-2012 Steven Levithan <stevenlevithan.com>
55 55 * Available under the MIT License
56 56 * ECMAScript compliant, uniform cross-browser split method
57 57 */
58 58
59 59 /**
60 60 * Splits a string into an array of strings using a regex or string
61 61 * separator. Matches of the separator are not included in the result array.
62 62 * However, if `separator` is a regex that contains capturing groups,
63 63 * backreferences are spliced into the result each time `separator` is
64 64 * matched. Fixes browser bugs compared to the native
65 65 * `String.prototype.split` and can be used reliably cross-browser.
66 66 * @param {String} str String to split.
67 67 * @param {RegExp|String} separator Regex or string to use for separating
68 68 * the string.
69 69 * @param {Number} [limit] Maximum number of items to include in the result
70 70 * array.
71 71 * @returns {Array} Array of substrings.
72 72 * @example
73 73 *
74 74 * // Basic use
75 75 * regex_split('a b c d', ' ');
76 76 * // -> ['a', 'b', 'c', 'd']
77 77 *
78 78 * // With limit
79 79 * regex_split('a b c d', ' ', 2);
80 80 * // -> ['a', 'b']
81 81 *
82 82 * // Backreferences in result array
83 83 * regex_split('..word1 word2..', /([a-z]+)(\d+)/i);
84 84 * // -> ['..', 'word', '1', ' ', 'word', '2', '..']
85 85 */
86 86 var regex_split = function (str, separator, limit) {
87 87 // If `separator` is not a regex, use `split`
88 88 if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
89 89 return split.call(str, separator, limit);
90 90 }
91 91 var output = [],
92 92 flags = (separator.ignoreCase ? "i" : "") +
93 93 (separator.multiline ? "m" : "") +
94 94 (separator.extended ? "x" : "") + // Proposed for ES6
95 95 (separator.sticky ? "y" : ""), // Firefox 3+
96 96 lastLastIndex = 0,
97 97 // Make `global` and avoid `lastIndex` issues by working with a copy
98 98 separator = new RegExp(separator.source, flags + "g"),
99 99 separator2, match, lastIndex, lastLength;
100 100 str += ""; // Type-convert
101 101
102 102 var compliantExecNpcg = typeof(/()??/.exec("")[1]) === "undefined";
103 103 if (!compliantExecNpcg) {
104 104 // Doesn't need flags gy, but they don't hurt
105 105 separator2 = new RegExp("^" + separator.source + "$(?!\\s)", flags);
106 106 }
107 107 /* Values for `limit`, per the spec:
108 108 * If undefined: 4294967295 // Math.pow(2, 32) - 1
109 109 * If 0, Infinity, or NaN: 0
110 110 * If positive number: limit = Math.floor(limit); if (limit > 4294967295) limit -= 4294967296;
111 111 * If negative number: 4294967296 - Math.floor(Math.abs(limit))
112 112 * If other: Type-convert, then use the above rules
113 113 */
114 114 limit = typeof(limit) === "undefined" ?
115 115 -1 >>> 0 : // Math.pow(2, 32) - 1
116 116 limit >>> 0; // ToUint32(limit)
117 117 while (match = separator.exec(str)) {
118 118 // `separator.lastIndex` is not reliable cross-browser
119 119 lastIndex = match.index + match[0].length;
120 120 if (lastIndex > lastLastIndex) {
121 121 output.push(str.slice(lastLastIndex, match.index));
122 122 // Fix browsers whose `exec` methods don't consistently return `undefined` for
123 123 // nonparticipating capturing groups
124 124 if (!compliantExecNpcg && match.length > 1) {
125 125 match[0].replace(separator2, function () {
126 126 for (var i = 1; i < arguments.length - 2; i++) {
127 127 if (typeof(arguments[i]) === "undefined") {
128 128 match[i] = undefined;
129 129 }
130 130 }
131 131 });
132 132 }
133 133 if (match.length > 1 && match.index < str.length) {
134 134 Array.prototype.push.apply(output, match.slice(1));
135 135 }
136 136 lastLength = match[0].length;
137 137 lastLastIndex = lastIndex;
138 138 if (output.length >= limit) {
139 139 break;
140 140 }
141 141 }
142 142 if (separator.lastIndex === match.index) {
143 143 separator.lastIndex++; // Avoid an infinite loop
144 144 }
145 145 }
146 146 if (lastLastIndex === str.length) {
147 147 if (lastLength || !separator.test("")) {
148 148 output.push("");
149 149 }
150 150 } else {
151 151 output.push(str.slice(lastLastIndex));
152 152 }
153 153 return output.length > limit ? output.slice(0, limit) : output;
154 154 };
155 155
156 156 //============================================================================
157 157 // End contributed Cross-browser RegEx Split
158 158 //============================================================================
159 159
160 160
161 161 var uuid = function () {
162 162 // http://www.ietf.org/rfc/rfc4122.txt
163 163 var s = [];
164 164 var hexDigits = "0123456789ABCDEF";
165 165 for (var i = 0; i < 32; i++) {
166 166 s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
167 167 }
168 168 s[12] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
169 169 s[16] = hexDigits.substr((s[16] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
170 170
171 171 var uuid = s.join("");
172 172 return uuid;
173 173 };
174 174
175 175
176 176 //Fix raw text to parse correctly in crazy XML
177 177 function xmlencode(string) {
178 178 return string.replace(/\&/g,'&'+'amp;')
179 179 .replace(/</g,'&'+'lt;')
180 180 .replace(/>/g,'&'+'gt;')
181 181 .replace(/\'/g,'&'+'apos;')
182 182 .replace(/\"/g,'&'+'quot;')
183 183 .replace(/`/g,'&'+'#96;');
184 184 }
185 185
186 186
187 187 //Map from terminal commands to CSS classes
188 188 var ansi_colormap = {
189 189 "01":"ansibold",
190 190
191 191 "30":"ansiblack",
192 192 "31":"ansired",
193 193 "32":"ansigreen",
194 194 "33":"ansiyellow",
195 195 "34":"ansiblue",
196 196 "35":"ansipurple",
197 197 "36":"ansicyan",
198 198 "37":"ansigray",
199 199
200 200 "40":"ansibgblack",
201 201 "41":"ansibgred",
202 202 "42":"ansibggreen",
203 203 "43":"ansibgyellow",
204 204 "44":"ansibgblue",
205 205 "45":"ansibgpurple",
206 206 "46":"ansibgcyan",
207 207 "47":"ansibggray"
208 208 };
209 209
210 210 function _process_numbers(attrs, numbers) {
211 211 // process ansi escapes
212 212 var n = numbers.shift();
213 213 if (ansi_colormap[n]) {
214 214 if ( ! attrs["class"] ) {
215 215 attrs["class"] = ansi_colormap[n];
216 216 } else {
217 217 attrs["class"] += " " + ansi_colormap[n];
218 218 }
219 219 } else if (n == "38" || n == "48") {
220 220 // VT100 256 color or 24 bit RGB
221 221 if (numbers.length < 2) {
222 222 console.log("Not enough fields for VT100 color", numbers);
223 223 return;
224 224 }
225 225
226 226 var index_or_rgb = numbers.shift();
227 227 var r,g,b;
228 228 if (index_or_rgb == "5") {
229 229 // 256 color
230 230 var idx = parseInt(numbers.shift());
231 231 if (idx < 16) {
232 232 // indexed ANSI
233 233 // ignore bright / non-bright distinction
234 234 idx = idx % 8;
235 235 var ansiclass = ansi_colormap[n[0] + (idx % 8).toString()];
236 236 if ( ! attrs["class"] ) {
237 237 attrs["class"] = ansiclass;
238 238 } else {
239 239 attrs["class"] += " " + ansiclass;
240 240 }
241 241 return;
242 242 } else if (idx < 232) {
243 243 // 216 color 6x6x6 RGB
244 244 idx = idx - 16;
245 245 b = idx % 6;
246 246 g = Math.floor(idx / 6) % 6;
247 247 r = Math.floor(idx / 36) % 6;
248 248 // convert to rgb
249 249 r = (r * 51);
250 250 g = (g * 51);
251 251 b = (b * 51);
252 252 } else {
253 253 // grayscale
254 254 idx = idx - 231;
255 255 // it's 1-24 and should *not* include black or white,
256 256 // so a 26 point scale
257 257 r = g = b = Math.floor(idx * 256 / 26);
258 258 }
259 259 } else if (index_or_rgb == "2") {
260 260 // Simple 24 bit RGB
261 261 if (numbers.length > 3) {
262 262 console.log("Not enough fields for RGB", numbers);
263 263 return;
264 264 }
265 265 r = numbers.shift();
266 266 g = numbers.shift();
267 267 b = numbers.shift();
268 268 } else {
269 269 console.log("unrecognized control", numbers);
270 270 return;
271 271 }
272 272 if (r !== undefined) {
273 273 // apply the rgb color
274 274 var line;
275 275 if (n == "38") {
276 276 line = "color: ";
277 277 } else {
278 278 line = "background-color: ";
279 279 }
280 280 line = line + "rgb(" + r + "," + g + "," + b + ");"
281 281 if ( !attrs["style"] ) {
282 282 attrs["style"] = line;
283 283 } else {
284 284 attrs["style"] += " " + line;
285 285 }
286 286 }
287 287 }
288 288 }
289 289
290 290 function ansispan(str) {
291 291 // ansispan function adapted from github.com/mmalecki/ansispan (MIT License)
292 292 // regular ansi escapes (using the table above)
293 293 return str.replace(/\033\[(0?[01]|22|39)?([;\d]+)?m/g, function(match, prefix, pattern) {
294 294 if (!pattern) {
295 295 // [(01|22|39|)m close spans
296 296 return "</span>";
297 297 }
298 298 // consume sequence of color escapes
299 299 var numbers = pattern.match(/\d+/g);
300 300 var attrs = {};
301 301 while (numbers.length > 0) {
302 302 _process_numbers(attrs, numbers);
303 303 }
304 304
305 305 var span = "<span ";
306 306 for (var attr in attrs) {
307 307 var value = attrs[attr];
308 308 span = span + " " + attr + '="' + attrs[attr] + '"';
309 309 }
310 310 return span + ">";
311 311 });
312 312 };
313 313
314 314 // Transform ANSI color escape codes into HTML <span> tags with css
315 315 // classes listed in the above ansi_colormap object. The actual color used
316 316 // are set in the css file.
317 317 function fixConsole(txt) {
318 318 txt = xmlencode(txt);
319 319 var re = /\033\[([\dA-Fa-f;]*?)m/;
320 320 var opened = false;
321 321 var cmds = [];
322 322 var opener = "";
323 323 var closer = "";
324 324
325 325 // Strip all ANSI codes that are not color related. Matches
326 326 // all ANSI codes that do not end with "m".
327 327 var ignored_re = /(?=(\033\[[\d;=]*[a-ln-zA-Z]{1}))\1(?!m)/g;
328 328 txt = txt.replace(ignored_re, "");
329 329
330 330 // color ansi codes
331 331 txt = ansispan(txt);
332 332 return txt;
333 333 }
334 334
335 335 // Remove chunks that should be overridden by the effect of
336 336 // carriage return characters
337 337 function fixCarriageReturn(txt) {
338 338 var tmp = txt;
339 339 do {
340 340 txt = tmp;
341 341 tmp = txt.replace(/\r+\n/gm, '\n'); // \r followed by \n --> newline
342 342 tmp = tmp.replace(/^.*\r+/gm, ''); // Other \r --> clear line
343 343 } while (tmp.length < txt.length);
344 344 return txt;
345 345 }
346 346
347 347 // Locate any URLs and convert them to a anchor tag
348 348 function autoLinkUrls(txt) {
349 349 return txt.replace(/(^|\s)(https?|ftp)(:[^'">\s]+)/gi,
350 350 "$1<a target=\"_blank\" href=\"$2$3\">$2$3</a>");
351 351 }
352 352
353 353 var points_to_pixels = function (points) {
354 354 // A reasonably good way of converting between points and pixels.
355 355 var test = $('<div style="display: none; width: 10000pt; padding:0; border:0;"></div>');
356 356 $(body).append(test);
357 357 var pixel_per_point = test.width()/10000;
358 358 test.remove();
359 359 return Math.floor(points*pixel_per_point);
360 360 };
361 361
362 362 var always_new = function (constructor) {
363 363 // wrapper around contructor to avoid requiring `var a = new constructor()`
364 364 // useful for passing constructors as callbacks,
365 365 // not for programmer laziness.
366 366 // from http://programmers.stackexchange.com/questions/118798
367 367 return function () {
368 368 var obj = Object.create(constructor.prototype);
369 369 constructor.apply(obj, arguments);
370 370 return obj;
371 371 };
372 372 };
373 373
374 374 var url_path_join = function () {
375 375 // join a sequence of url components with '/'
376 376 var url = '';
377 377 for (var i = 0; i < arguments.length; i++) {
378 378 if (arguments[i] === '') {
379 379 continue;
380 380 }
381 381 if (url.length > 0 && url[url.length-1] != '/') {
382 382 url = url + '/' + arguments[i];
383 383 } else {
384 384 url = url + arguments[i];
385 385 }
386 386 }
387 387 url = url.replace(/\/\/+/, '/');
388 388 return url;
389 389 };
390 390
391 391 var parse_url = function (url) {
392 392 // an `a` element with an href allows attr-access to the parsed segments of a URL
393 393 // a = parse_url("http://localhost:8888/path/name#hash")
394 394 // a.protocol = "http:"
395 395 // a.host = "localhost:8888"
396 396 // a.hostname = "localhost"
397 397 // a.port = 8888
398 398 // a.pathname = "/path/name"
399 399 // a.hash = "#hash"
400 400 var a = document.createElement("a");
401 401 a.href = url;
402 402 return a;
403 403 };
404 404
405 405 var encode_uri_components = function (uri) {
406 406 // encode just the components of a multi-segment uri,
407 407 // leaving '/' separators
408 408 return uri.split('/').map(encodeURIComponent).join('/');
409 409 };
410 410
411 411 var url_join_encode = function () {
412 412 // join a sequence of url components with '/',
413 413 // encoding each component with encodeURIComponent
414 414 return encode_uri_components(url_path_join.apply(null, arguments));
415 415 };
416 416
417 417
418 418 var splitext = function (filename) {
419 419 // mimic Python os.path.splitext
420 420 // Returns ['base', '.ext']
421 421 var idx = filename.lastIndexOf('.');
422 422 if (idx > 0) {
423 423 return [filename.slice(0, idx), filename.slice(idx)];
424 424 } else {
425 425 return [filename, ''];
426 426 }
427 427 };
428 428
429 429
430 430 var escape_html = function (text) {
431 431 // escape text to HTML
432 432 return $("<div/>").text(text).html();
433 433 }
434 434
435 435
436 436 var get_body_data = function(key) {
437 437 // get a url-encoded item from body.data and decode it
438 438 // we should never have any encoded URLs anywhere else in code
439 439 // until we are building an actual request
440 440 return decodeURIComponent($('body').data(key));
441 441 };
442 442
443 443
444 444 // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript
445 445 var browser = (function() {
446 446 if (typeof navigator === 'undefined') {
447 447 // navigator undefined in node
448 448 return 'None';
449 449 }
450 450 var N= navigator.appName, ua= navigator.userAgent, tem;
451 451 var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);
452 452 if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1];
453 453 M= M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
454 454 return M;
455 455 })();
456 456
457 457 // http://stackoverflow.com/questions/11219582/how-to-detect-my-browser-version-and-operating-system-using-javascript
458 458 var platform = (function () {
459 459 if (typeof navigator === 'undefined') {
460 460 // navigator undefined in node
461 461 return 'None';
462 462 }
463 463 var OSName="None";
464 464 if (navigator.appVersion.indexOf("Win")!=-1) OSName="Windows";
465 465 if (navigator.appVersion.indexOf("Mac")!=-1) OSName="MacOS";
466 466 if (navigator.appVersion.indexOf("X11")!=-1) OSName="UNIX";
467 467 if (navigator.appVersion.indexOf("Linux")!=-1) OSName="Linux";
468 468 return OSName
469 469 })();
470 470
471 471 var is_or_has = function (a, b) {
472 472 // Is b a child of a or a itself?
473 473 return a.has(b).length !==0 || a.is(b);
474 474 }
475 475
476 476 var is_focused = function (e) {
477 477 // Is element e, or one of its children focused?
478 478 e = $(e);
479 479 var target = $(document.activeElement);
480 480 if (target.length > 0) {
481 481 if (is_or_has(e, target)) {
482 482 return true;
483 483 } else {
484 484 return false;
485 485 }
486 486 } else {
487 487 return false;
488 488 }
489 489 }
490 490
491 var get_attr_names = function (e) {
492 // Get the names of all the HTML attributes of the element e.
493 var el = $(e)[0];
494 var arr = [];
495 for (var i=0, attrs=el.attributes, l=attrs.length; i<l; i++){
496 arr.push(attrs.item(i).nodeName);
497 }
498 return arr;
499 }
491 500
492 501 return {
493 502 regex_split : regex_split,
494 503 uuid : uuid,
495 504 fixConsole : fixConsole,
496 505 fixCarriageReturn : fixCarriageReturn,
497 506 autoLinkUrls : autoLinkUrls,
498 507 points_to_pixels : points_to_pixels,
499 508 get_body_data : get_body_data,
500 509 parse_url : parse_url,
501 510 url_path_join : url_path_join,
502 511 url_join_encode : url_join_encode,
503 512 encode_uri_components : encode_uri_components,
504 513 splitext : splitext,
505 514 escape_html : escape_html,
506 515 always_new : always_new,
507 516 browser : browser,
508 517 platform: platform,
509 518 is_or_has : is_or_has,
510 is_focused : is_focused
519 is_focused : is_focused,
520 get_attr_names: get_attr_names
511 521 };
512 522
513 523 }(IPython));
514 524
@@ -1,561 +1,566 b''
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 // TextCell
10 10 //============================================================================
11 11
12 12
13 13
14 14 /**
15 15 A module that allow to create different type of Text Cell
16 16 @module IPython
17 17 @namespace IPython
18 18 */
19 19 var IPython = (function (IPython) {
20 20 "use strict";
21 21
22 22 // TextCell base class
23 <<<<<<< HEAD
23 24 var keycodes = IPython.keyboard.keycodes;
25 =======
26 var key = IPython.utils.keycodes;
27 var security = IPython.security;
28 >>>>>>> 8e23f06... Adding security.js with 1st attempt at is_safe.
24 29
25 30 /**
26 31 * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text'
27 32 * cell start as not redered.
28 33 *
29 34 * @class TextCell
30 35 * @constructor TextCell
31 36 * @extend IPython.Cell
32 37 * @param {object|undefined} [options]
33 38 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config
34 39 * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass)
35 40 */
36 41 var TextCell = function (options) {
37 42 // in all TextCell/Cell subclasses
38 43 // do not assign most of members here, just pass it down
39 44 // in the options dict potentially overwriting what you wish.
40 45 // they will be assigned in the base class.
41 46
42 47 // we cannot put this as a class key as it has handle to "this".
43 48 var cm_overwrite_options = {
44 49 onKeyEvent: $.proxy(this.handle_keyevent,this)
45 50 };
46 51
47 52 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
48 53
49 54 this.cell_type = this.cell_type || 'text';
50 55
51 56 IPython.Cell.apply(this, [options]);
52 57
53 58 this.rendered = false;
54 59 };
55 60
56 61 TextCell.prototype = new IPython.Cell();
57 62
58 63 TextCell.options_default = {
59 64 cm_config : {
60 65 extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"},
61 66 mode: 'htmlmixed',
62 67 lineWrapping : true,
63 68 }
64 69 };
65 70
66 71
67 72 /**
68 73 * Create the DOM element of the TextCell
69 74 * @method create_element
70 75 * @private
71 76 */
72 77 TextCell.prototype.create_element = function () {
73 78 IPython.Cell.prototype.create_element.apply(this, arguments);
74 79
75 80 var cell = $("<div>").addClass('cell text_cell border-box-sizing');
76 81 cell.attr('tabindex','2');
77 82
78 83 var prompt = $('<div/>').addClass('prompt input_prompt');
79 84 cell.append(prompt);
80 85 var inner_cell = $('<div/>').addClass('inner_cell');
81 86 this.celltoolbar = new IPython.CellToolbar(this);
82 87 inner_cell.append(this.celltoolbar.element);
83 88 var input_area = $('<div/>').addClass('input_area');
84 89 this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config);
85 90 // The tabindex=-1 makes this div focusable.
86 91 var render_area = $('<div/>').addClass('text_cell_render border-box-sizing').
87 92 addClass('rendered_html').attr('tabindex','-1');
88 93 inner_cell.append(input_area).append(render_area);
89 94 cell.append(inner_cell);
90 95 this.element = cell;
91 96 };
92 97
93 98
94 99 /**
95 100 * Bind the DOM evet to cell actions
96 101 * Need to be called after TextCell.create_element
97 102 * @private
98 103 * @method bind_event
99 104 */
100 105 TextCell.prototype.bind_events = function () {
101 106 IPython.Cell.prototype.bind_events.apply(this);
102 107 var that = this;
103 108
104 109 this.element.dblclick(function () {
105 110 if (that.selected === false) {
106 111 $([IPython.events]).trigger('select.Cell', {'cell':that});
107 112 }
108 113 var cont = that.unrender();
109 114 if (cont) {
110 115 that.focus_editor();
111 116 }
112 117 });
113 118 };
114 119
115 120 TextCell.prototype.handle_keyevent = function (editor, event) {
116 121
117 122 // console.log('CM', this.mode, event.which, event.type)
118 123
119 124 if (this.mode === 'command') {
120 125 return true;
121 126 } else if (this.mode === 'edit') {
122 127 return this.handle_codemirror_keyevent(editor, event);
123 128 }
124 129 };
125 130
126 131 /**
127 132 * This method gets called in CodeMirror's onKeyDown/onKeyPress
128 133 * handlers and is used to provide custom key handling.
129 134 *
130 135 * Subclass should override this method to have custom handeling
131 136 *
132 137 * @method handle_codemirror_keyevent
133 138 * @param {CodeMirror} editor - The codemirror instance bound to the cell
134 139 * @param {event} event -
135 140 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
136 141 */
137 142 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
138 143 var that = this;
139 144
140 145 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) {
141 146 // Always ignore shift-enter in CodeMirror as we handle it.
142 147 return true;
143 148 } else if (event.which === keycodes.up && event.type === 'keydown') {
144 149 // If we are not at the top, let CM handle the up arrow and
145 150 // prevent the global keydown handler from handling it.
146 151 if (!that.at_top()) {
147 152 event.stop();
148 153 return false;
149 154 } else {
150 155 return true;
151 156 };
152 157 } else if (event.which === keycodes.down && event.type === 'keydown') {
153 158 // If we are not at the bottom, let CM handle the down arrow and
154 159 // prevent the global keydown handler from handling it.
155 160 if (!that.at_bottom()) {
156 161 event.stop();
157 162 return false;
158 163 } else {
159 164 return true;
160 165 };
161 166 } else if (event.which === keycodes.esc && event.type === 'keydown') {
162 167 if (that.code_mirror.options.keyMap === "vim-insert") {
163 168 // vim keyMap is active and in insert mode. In this case we leave vim
164 169 // insert mode, but remain in notebook edit mode.
165 170 // Let' CM handle this event and prevent global handling.
166 171 event.stop();
167 172 return false;
168 173 } else {
169 174 // vim keyMap is not active. Leave notebook edit mode.
170 175 // Don't let CM handle the event, defer to global handling.
171 176 return true;
172 177 }
173 178 }
174 179 return false;
175 180 };
176 181
177 182 // Cell level actions
178 183
179 184 TextCell.prototype.select = function () {
180 185 var cont = IPython.Cell.prototype.select.apply(this);
181 186 if (cont) {
182 187 if (this.mode === 'edit') {
183 188 this.code_mirror.refresh();
184 189 }
185 190 }
186 191 return cont;
187 192 };
188 193
189 194 TextCell.prototype.unrender = function () {
190 195 if (this.read_only) return;
191 196 var cont = IPython.Cell.prototype.unrender.apply(this);
192 197 if (cont) {
193 198 var text_cell = this.element;
194 199 var output = text_cell.find("div.text_cell_render");
195 200 output.hide();
196 201 text_cell.find('div.input_area').show();
197 202 if (this.get_text() === this.placeholder) {
198 203 this.set_text('');
199 204 }
200 205 this.refresh();
201 206 }
202 207 return cont;
203 208 };
204 209
205 210 TextCell.prototype.execute = function () {
206 211 this.render();
207 212 };
208 213
209 214 /**
210 215 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
211 216 * @method get_text
212 217 * @retrun {string} CodeMirror current text value
213 218 */
214 219 TextCell.prototype.get_text = function() {
215 220 return this.code_mirror.getValue();
216 221 };
217 222
218 223 /**
219 224 * @param {string} text - Codemiror text value
220 225 * @see TextCell#get_text
221 226 * @method set_text
222 227 * */
223 228 TextCell.prototype.set_text = function(text) {
224 229 this.code_mirror.setValue(text);
225 230 this.code_mirror.refresh();
226 231 };
227 232
228 233 /**
229 234 * setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}}
230 235 * @method get_rendered
231 236 * @return {html} html of rendered element
232 237 * */
233 238 TextCell.prototype.get_rendered = function() {
234 239 return this.element.find('div.text_cell_render').html();
235 240 };
236 241
237 242 /**
238 243 * @method set_rendered
239 244 */
240 245 TextCell.prototype.set_rendered = function(text) {
241 246 this.element.find('div.text_cell_render').html(text);
242 247 };
243 248
244 249 /**
245 250 * @method at_top
246 251 * @return {Boolean}
247 252 */
248 253 TextCell.prototype.at_top = function () {
249 254 if (this.rendered) {
250 255 return true;
251 256 } else {
252 257 var cursor = this.code_mirror.getCursor();
253 258 if (cursor.line === 0 && cursor.ch === 0) {
254 259 return true;
255 260 } else {
256 261 return false;
257 262 }
258 263 }
259 264 };
260 265
261 266 /**
262 267 * @method at_bottom
263 268 * @return {Boolean}
264 269 * */
265 270 TextCell.prototype.at_bottom = function () {
266 271 if (this.rendered) {
267 272 return true;
268 273 } else {
269 274 var cursor = this.code_mirror.getCursor();
270 275 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
271 276 return true;
272 277 } else {
273 278 return false;
274 279 }
275 280 }
276 281 };
277 282
278 283 /**
279 284 * Create Text cell from JSON
280 285 * @param {json} data - JSON serialized text-cell
281 286 * @method fromJSON
282 287 */
283 288 TextCell.prototype.fromJSON = function (data) {
284 289 IPython.Cell.prototype.fromJSON.apply(this, arguments);
285 290 if (data.cell_type === this.cell_type) {
286 291 if (data.source !== undefined) {
287 292 this.set_text(data.source);
288 293 // make this value the starting point, so that we can only undo
289 294 // to this state, instead of a blank cell
290 295 this.code_mirror.clearHistory();
291 296 // TODO: This HTML needs to be treated as potentially dangerous
292 297 // user input and should be handled before set_rendered.
293 298 this.set_rendered(data.rendered || '');
294 299 this.rendered = false;
295 300 this.render();
296 301 }
297 302 }
298 303 };
299 304
300 305 /** Generate JSON from cell
301 306 * @return {object} cell data serialised to json
302 307 */
303 308 TextCell.prototype.toJSON = function () {
304 309 var data = IPython.Cell.prototype.toJSON.apply(this);
305 310 data.source = this.get_text();
306 311 if (data.source == this.placeholder) {
307 312 data.source = "";
308 313 }
309 314 return data;
310 315 };
311 316
312 317
313 318 /**
314 319 * @class MarkdownCell
315 320 * @constructor MarkdownCell
316 321 * @extends IPython.HTMLCell
317 322 */
318 323 var MarkdownCell = function (options) {
319 324 options = this.mergeopt(MarkdownCell, options);
320 325
321 326 this.cell_type = 'markdown';
322 327 TextCell.apply(this, [options]);
323 328 };
324 329
325 330 MarkdownCell.options_default = {
326 331 cm_config: {
327 332 mode: 'gfm'
328 333 },
329 334 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
330 335 };
331 336
332 337 MarkdownCell.prototype = new TextCell();
333 338
334 339 /**
335 340 * @method render
336 341 */
337 342 MarkdownCell.prototype.render = function () {
338 343 var cont = IPython.TextCell.prototype.render.apply(this);
339 344 if (cont) {
340 345 var text = this.get_text();
341 346 var math = null;
342 347 if (text === "") { text = this.placeholder; }
343 348 var text_and_math = IPython.mathjaxutils.remove_math(text);
344 349 text = text_and_math[0];
345 350 math = text_and_math[1];
346 351 var html = marked.parser(marked.lexer(text));
347 352 html = $(IPython.mathjaxutils.replace_math(html, math));
348 353 // Links in markdown cells should open in new tabs.
349 354 html.find("a[href]").not('[href^="#"]').attr("target", "_blank");
350 355 try {
351 356 // TODO: This HTML needs to be treated as potentially dangerous
352 357 // user input and should be handled before set_rendered.
353 358 this.set_rendered(html);
354 359 } catch (e) {
355 360 console.log("Error running Javascript in Markdown:");
356 361 console.log(e);
357 362 this.set_rendered(
358 363 $("<div/>")
359 364 .append($("<div/>").text('Error rendering Markdown!').addClass("js-error"))
360 365 .append($("<div/>").text(e.toString()).addClass("js-error"))
361 366 .html()
362 367 );
363 368 }
364 369 this.element.find('div.input_area').hide();
365 370 this.element.find("div.text_cell_render").show();
366 371 this.typeset();
367 372 }
368 373 return cont;
369 374 };
370 375
371 376
372 377 // RawCell
373 378
374 379 /**
375 380 * @class RawCell
376 381 * @constructor RawCell
377 382 * @extends IPython.TextCell
378 383 */
379 384 var RawCell = function (options) {
380 385
381 386 options = this.mergeopt(RawCell,options);
382 387 TextCell.apply(this, [options]);
383 388 this.cell_type = 'raw';
384 389 // RawCell should always hide its rendered div
385 390 this.element.find('div.text_cell_render').hide();
386 391 };
387 392
388 393 RawCell.options_default = {
389 394 placeholder : "Write raw LaTeX or other formats here, for use with nbconvert.\n" +
390 395 "It will not be rendered in the notebook.\n" +
391 396 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
392 397 };
393 398
394 399 RawCell.prototype = new TextCell();
395 400
396 401 /** @method bind_events **/
397 402 RawCell.prototype.bind_events = function () {
398 403 TextCell.prototype.bind_events.apply(this);
399 404 var that = this;
400 405 this.element.focusout(function() {
401 406 that.auto_highlight();
402 407 });
403 408 };
404 409
405 410 /**
406 411 * Trigger autodetection of highlight scheme for current cell
407 412 * @method auto_highlight
408 413 */
409 414 RawCell.prototype.auto_highlight = function () {
410 415 this._auto_highlight(IPython.config.raw_cell_highlight);
411 416 };
412 417
413 418 /** @method render **/
414 419 RawCell.prototype.render = function () {
415 420 // Make sure that this cell type can never be rendered
416 421 if (this.rendered) {
417 422 this.unrender();
418 423 }
419 424 var text = this.get_text();
420 425 if (text === "") { text = this.placeholder; }
421 426 this.set_text(text);
422 427 };
423 428
424 429
425 430 /**
426 431 * @class HeadingCell
427 432 * @extends IPython.TextCell
428 433 */
429 434
430 435 /**
431 436 * @constructor HeadingCell
432 437 * @extends IPython.TextCell
433 438 */
434 439 var HeadingCell = function (options) {
435 440 options = this.mergeopt(HeadingCell, options);
436 441
437 442 this.level = 1;
438 443 this.cell_type = 'heading';
439 444 TextCell.apply(this, [options]);
440 445
441 446 /**
442 447 * heading level of the cell, use getter and setter to access
443 448 * @property level
444 449 */
445 450 };
446 451
447 452 HeadingCell.options_default = {
448 453 placeholder: "Type Heading Here"
449 454 };
450 455
451 456 HeadingCell.prototype = new TextCell();
452 457
453 458 /** @method fromJSON */
454 459 HeadingCell.prototype.fromJSON = function (data) {
455 460 if (data.level !== undefined){
456 461 this.level = data.level;
457 462 }
458 463 TextCell.prototype.fromJSON.apply(this, arguments);
459 464 };
460 465
461 466
462 467 /** @method toJSON */
463 468 HeadingCell.prototype.toJSON = function () {
464 469 var data = TextCell.prototype.toJSON.apply(this);
465 470 data.level = this.get_level();
466 471 return data;
467 472 };
468 473
469 474 /**
470 475 * can the cell be split into two cells
471 476 * @method is_splittable
472 477 **/
473 478 HeadingCell.prototype.is_splittable = function () {
474 479 return false;
475 480 };
476 481
477 482
478 483 /**
479 484 * can the cell be merged with other cells
480 485 * @method is_mergeable
481 486 **/
482 487 HeadingCell.prototype.is_mergeable = function () {
483 488 return false;
484 489 };
485 490
486 491 /**
487 492 * Change heading level of cell, and re-render
488 493 * @method set_level
489 494 */
490 495 HeadingCell.prototype.set_level = function (level) {
491 496 this.level = level;
492 497 if (this.rendered) {
493 498 this.rendered = false;
494 499 this.render();
495 500 }
496 501 };
497 502
498 503 /** The depth of header cell, based on html (h1 to h6)
499 504 * @method get_level
500 505 * @return {integer} level - for 1 to 6
501 506 */
502 507 HeadingCell.prototype.get_level = function () {
503 508 return this.level;
504 509 };
505 510
506 511
507 512 HeadingCell.prototype.set_rendered = function (html) {
508 513 this.element.find("div.text_cell_render").html(html);
509 514 };
510 515
511 516
512 517 HeadingCell.prototype.get_rendered = function () {
513 518 var r = this.element.find("div.text_cell_render");
514 519 return r.children().first().html();
515 520 };
516 521
517 522
518 523 HeadingCell.prototype.render = function () {
519 524 var cont = IPython.TextCell.prototype.render.apply(this);
520 525 if (cont) {
521 526 var text = this.get_text();
522 527 var math = null;
523 528 // Markdown headings must be a single line
524 529 text = text.replace(/\n/g, ' ');
525 530 if (text === "") { text = this.placeholder; }
526 531 text = Array(this.level + 1).join("#") + " " + text;
527 532 var text_and_math = IPython.mathjaxutils.remove_math(text);
528 533 text = text_and_math[0];
529 534 math = text_and_math[1];
530 535 var html = marked.parser(marked.lexer(text));
531 536 var h = $(IPython.mathjaxutils.replace_math(html, math));
532 537 // add id and linkback anchor
533 538 var hash = h.text().replace(/ /g, '-');
534 539 h.attr('id', hash);
535 540 h.append(
536 541 $('<a/>')
537 542 .addClass('anchor-link')
538 543 .attr('href', '#' + hash)
539 544 .text('ΒΆ')
540 545 );
541 546 // TODO: This HTML needs to be treated as potentially dangerous
542 547 // user input and should be handled before set_rendered.
543 548 this.set_rendered(h);
544 549 this.typeset();
545 550 this.element.find('div.input_area').hide();
546 551 this.element.find("div.text_cell_render").show();
547 552
548 553 }
549 554 return cont;
550 555 };
551 556
552 557 IPython.TextCell = TextCell;
553 558 IPython.MarkdownCell = MarkdownCell;
554 559 IPython.RawCell = RawCell;
555 560 IPython.HeadingCell = HeadingCell;
556 561
557 562
558 563 return IPython;
559 564
560 565 }(IPython));
561 566
@@ -1,353 +1,354 b''
1 1 {% extends "page.html" %}
2 2
3 3 {% block stylesheet %}
4 4
5 5 {% if mathjax_url %}
6 6 <script type="text/javascript" src="{{mathjax_url}}?config=TeX-AMS_HTML-full&delayStartupUntil=configured" charset="utf-8"></script>
7 7 {% endif %}
8 8 <script type="text/javascript">
9 9 // MathJax disabled, set as null to distingish from *missing* MathJax,
10 10 // where it will be undefined, and should prompt a dialog later.
11 11 window.mathjax_url = "{{mathjax_url}}";
12 12 </script>
13 13
14 14 <link rel="stylesheet" href="{{ static_url("components/codemirror/lib/codemirror.css") }}">
15 15
16 16 {{super()}}
17 17
18 18 <link rel="stylesheet" href="{{ static_url("notebook/css/override.css") }}" type="text/css" />
19 19
20 20 {% endblock %}
21 21
22 22 {% block params %}
23 23
24 24 data-project="{{project}}"
25 25 data-base-url="{{base_url}}"
26 26 data-notebook-name="{{notebook_name}}"
27 27 data-notebook-path="{{notebook_path}}"
28 28 class="notebook_app"
29 29
30 30 {% endblock %}
31 31
32 32
33 33 {% block header %}
34 34
35 35 <span id="save_widget" class="nav pull-left">
36 36 <span id="notebook_name"></span>
37 37 <span id="checkpoint_status"></span>
38 38 <span id="autosave_status"></span>
39 39 </span>
40 40
41 41 {% endblock %}
42 42
43 43
44 44 {% block site %}
45 45
46 46 <div id="menubar-container" class="container">
47 47 <div id="menubar">
48 48 <div class="navbar">
49 49 <div class="navbar-inner">
50 50 <div class="container">
51 51 <ul id="menus" class="nav">
52 52 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">File</a>
53 53 <ul id="file_menu" class="dropdown-menu">
54 54 <li id="new_notebook"
55 55 title="Make a new notebook (Opens a new window)">
56 56 <a href="#">New</a></li>
57 57 <li id="open_notebook"
58 58 title="Opens a new window with the Dashboard view">
59 59 <a href="#">Open...</a></li>
60 60 <!-- <hr/> -->
61 61 <li class="divider"></li>
62 62 <li id="copy_notebook"
63 63 title="Open a copy of this notebook's contents and start a new kernel">
64 64 <a href="#">Make a Copy...</a></li>
65 65 <li id="rename_notebook"><a href="#">Rename...</a></li>
66 66 <li id="save_checkpoint"><a href="#">Save and Checkpoint</a></li>
67 67 <!-- <hr/> -->
68 68 <li class="divider"></li>
69 69 <li id="restore_checkpoint" class="dropdown-submenu"><a href="#">Revert to Checkpoint</a>
70 70 <ul class="dropdown-menu">
71 71 <li><a href="#"></a></li>
72 72 <li><a href="#"></a></li>
73 73 <li><a href="#"></a></li>
74 74 <li><a href="#"></a></li>
75 75 <li><a href="#"></a></li>
76 76 </ul>
77 77 </li>
78 78 <li class="divider"></li>
79 79 <li id="print_preview"><a href="#">Print Preview</a></li>
80 80 <li class="dropdown-submenu"><a href="#">Download as</a>
81 81 <ul class="dropdown-menu">
82 82 <li id="download_ipynb"><a href="#">IPython Notebook (.ipynb)</a></li>
83 83 <li id="download_py"><a href="#">Python (.py)</a></li>
84 84 <li id="download_html"><a href="#">HTML (.html)</a></li>
85 85 <li id="download_rst"><a href="#">reST (.rst)</a></li>
86 86 </ul>
87 87 </li>
88 88 <li class="divider"></li>
89 89
90 90 <li id="kill_and_exit"
91 91 title="Shutdown this notebook's kernel, and close this window">
92 92 <a href="#" >Close and halt</a></li>
93 93 </ul>
94 94 </li>
95 95 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Edit</a>
96 96 <ul id="edit_menu" class="dropdown-menu">
97 97 <li id="cut_cell"><a href="#">Cut Cell</a></li>
98 98 <li id="copy_cell"><a href="#">Copy Cell</a></li>
99 99 <li id="paste_cell_above" class="disabled"><a href="#">Paste Cell Above</a></li>
100 100 <li id="paste_cell_below" class="disabled"><a href="#">Paste Cell Below</a></li>
101 101 <li id="paste_cell_replace" class="disabled"><a href="#">Paste Cell &amp; Replace</a></li>
102 102 <li id="delete_cell"><a href="#">Delete Cell</a></li>
103 103 <li id="undelete_cell" class="disabled"><a href="#">Undo Delete Cell</a></li>
104 104 <li class="divider"></li>
105 105 <li id="split_cell"><a href="#">Split Cell</a></li>
106 106 <li id="merge_cell_above"><a href="#">Merge Cell Above</a></li>
107 107 <li id="merge_cell_below"><a href="#">Merge Cell Below</a></li>
108 108 <li class="divider"></li>
109 109 <li id="move_cell_up"><a href="#">Move Cell Up</a></li>
110 110 <li id="move_cell_down"><a href="#">Move Cell Down</a></li>
111 111 <li class="divider"></li>
112 112 <li id="edit_nb_metadata"><a href="#">Edit Notebook Metadata</a></li>
113 113 </ul>
114 114 </li>
115 115 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">View</a>
116 116 <ul id="view_menu" class="dropdown-menu">
117 117 <li id="toggle_header"
118 118 title="Show/Hide the IPython Notebook logo and notebook title (above menu bar)">
119 119 <a href="#">Toggle Header</a></li>
120 120 <li id="toggle_toolbar"
121 121 title="Show/Hide the action icons (below menu bar)">
122 122 <a href="#">Toggle Toolbar</a></li>
123 123 </ul>
124 124 </li>
125 125 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Insert</a>
126 126 <ul id="insert_menu" class="dropdown-menu">
127 127 <li id="insert_cell_above"
128 128 title="Insert an empty Code cell above the currently active cell">
129 129 <a href="#">Insert Cell Above</a></li>
130 130 <li id="insert_cell_below"
131 131 title="Insert an empty Code cell below the currently active cell">
132 132 <a href="#">Insert Cell Below</a></li>
133 133 </ul>
134 134 </li>
135 135 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Cell</a>
136 136 <ul id="cell_menu" class="dropdown-menu">
137 137 <li id="run_cell" title="Run this cell, and move cursor to the next one">
138 138 <a href="#">Run</a></li>
139 139 <li id="run_cell_select_below" title="Run this cell, select below">
140 140 <a href="#">Run and Select Below</a></li>
141 141 <li id="run_cell_insert_below" title="Run this cell, insert below">
142 142 <a href="#">Run and Insert Below</a></li>
143 143 <li id="run_all_cells" title="Run all cells in the notebook">
144 144 <a href="#">Run All</a></li>
145 145 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
146 146 <a href="#">Run All Above</a></li>
147 147 <li id="run_all_cells_below" title="Run this cell and all cells below it">
148 148 <a href="#">Run All Below</a></li>
149 149 <li class="divider"></li>
150 150 <li id="change_cell_type" class="dropdown-submenu"
151 151 title="All cells in the notebook have a cell type. By default, new cells are created as 'Code' cells">
152 152 <a href="#">Cell Type</a>
153 153 <ul class="dropdown-menu">
154 154 <li id="to_code"
155 155 title="Contents will be sent to the kernel for execution, and output will display in the footer of cell">
156 156 <a href="#">Code</a></li>
157 157 <li id="to_markdown"
158 158 title="Contents will be rendered as HTML and serve as explanatory text">
159 159 <a href="#">Markdown</a></li>
160 160 <li id="to_raw"
161 161 title="Contents will pass through nbconvert unmodified">
162 162 <a href="#">Raw NBConvert</a></li>
163 163 <li id="to_heading1"><a href="#">Heading 1</a></li>
164 164 <li id="to_heading2"><a href="#">Heading 2</a></li>
165 165 <li id="to_heading3"><a href="#">Heading 3</a></li>
166 166 <li id="to_heading4"><a href="#">Heading 4</a></li>
167 167 <li id="to_heading5"><a href="#">Heading 5</a></li>
168 168 <li id="to_heading6"><a href="#">Heading 6</a></li>
169 169 </ul>
170 170 </li>
171 171 <li class="divider"></li>
172 172 <li id="current_outputs" class="dropdown-submenu"><a href="#">Current Output</a>
173 173 <ul class="dropdown-menu">
174 174 <li id="toggle_current_output"
175 175 title="Hide/Show the output of the current cell">
176 176 <a href="#">Toggle</a>
177 177 </li>
178 178 <li id="toggle_current_output_scroll"
179 179 title="Scroll the output of the current cell">
180 180 <a href="#">Toggle Scrolling</a>
181 181 </li>
182 182 <li id="clear_current_output"
183 183 title="Clear the output of the current cell">
184 184 <a href="#">Clear</a>
185 185 </li>
186 186 </ul>
187 187 </li>
188 188 <li id="all_outputs" class="dropdown-submenu"><a href="#">All Output</a>
189 189 <ul class="dropdown-menu">
190 190 <li id="toggle_all_output"
191 191 title="Hide/Show the output of all cells">
192 192 <a href="#">Toggle</a>
193 193 </li>
194 194 <li id="toggle_all_output_scroll"
195 195 title="Scroll the output of all cells">
196 196 <a href="#">Toggle Scrolling</a>
197 197 </li>
198 198 <li id="clear_all_output"
199 199 title="Clear the output of all cells">
200 200 <a href="#">Clear</a>
201 201 </li>
202 202 </ul>
203 203 </li>
204 204 </ul>
205 205 </li>
206 206 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Kernel</a>
207 207 <ul id="kernel_menu" class="dropdown-menu">
208 208 <li id="int_kernel"
209 209 title="Send KeyboardInterrupt (CTRL-C) to the Kernel">
210 210 <a href="#">Interrupt</a></li>
211 211 <li id="restart_kernel"
212 212 title="Restart the Kernel">
213 213 <a href="#">Restart</a></li>
214 214 </ul>
215 215 </li>
216 216 <li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">Help</a>
217 217 <ul id="help_menu" class="dropdown-menu">
218 218 <li id="keyboard_shortcuts" title="Opens a tooltip with all keyboard shortcuts"><a href="#">Keyboard Shortcuts</a></li>
219 219 <li class="divider"></li>
220 220 {% set
221 221 sections = (
222 222 (
223 223 ("http://ipython.org/documentation.html","IPython Help",True),
224 224 ("http://nbviewer.ipython.org/github/ipython/ipython/tree/master/examples/notebooks/", "Notebook Examples", True),
225 225 ("http://ipython.org/ipython-doc/stable/interactive/notebook.html","Notebook Help",True),
226 226 ("http://ipython.org/ipython-doc/dev/interactive/cm_keyboard.html","Editor Shortcuts",True),
227 227 ),(
228 228 ("http://docs.python.org","Python",True),
229 229 ("http://docs.scipy.org/doc/numpy/reference/","NumPy",True),
230 230 ("http://docs.scipy.org/doc/scipy/reference/","SciPy",True),
231 231 ("http://matplotlib.org/contents.html","Matplotlib",True),
232 232 ("http://docs.sympy.org/dev/index.html","SymPy",True),
233 233 ("http://pandas.pydata.org/pandas-docs/stable/","pandas", True)
234 234 )
235 235 )
236 236 %}
237 237
238 238 {% for helplinks in sections %}
239 239 {% for link in helplinks %}
240 240 <li><a href="{{link[0]}}" {{'target="_blank" title="Opens in a new window"' if link[2]}}>
241 241 {{'<i class="icon-external-link menu-icon pull-right"></i>' if link[2]}}
242 242 {{link[1]}}
243 243 </a></li>
244 244 {% endfor %}
245 245 {% if not loop.last %}
246 246 <li class="divider"></li>
247 247 {% endif %}
248 248 {% endfor %}
249 249 </li>
250 250 </ul>
251 251 </li>
252 252 </ul>
253 253 <div id="kernel_indicator" class="indicator_area pull-right">
254 254 <i id="kernel_indicator_icon"></i>
255 255 </div>
256 256 <div id="modal_indicator" class="indicator_area pull-right">
257 257 <i id="modal_indicator_icon"></i>
258 258 </div>
259 259 <div id="notification_area"></div>
260 260 </div>
261 261 </div>
262 262 </div>
263 263 </div>
264 264 <div id="maintoolbar" class="navbar">
265 265 <div class="toolbar-inner navbar-inner navbar-nobg">
266 266 <div id="maintoolbar-container" class="container"></div>
267 267 </div>
268 268 </div>
269 269 </div>
270 270
271 271 <div id="ipython-main-app">
272 272
273 273 <div id="notebook_panel">
274 274 <div id="notebook"></div>
275 275 <div id="pager_splitter"></div>
276 276 <div id="pager">
277 277 <div id='pager_button_area'>
278 278 </div>
279 279 <div id="pager-container" class="container"></div>
280 280 </div>
281 281 </div>
282 282
283 283 </div>
284 284 <div id='tooltip' class='ipython_tooltip' style='display:none'></div>
285 285
286 286
287 287 {% endblock %}
288 288
289 289
290 290 {% block script %}
291 291
292 292 {{super()}}
293 293
294 294 <script src="{{ static_url("components/codemirror/lib/codemirror.js") }}" charset="utf-8"></script>
295 295 <script type="text/javascript">
296 296 CodeMirror.modeURL = "{{ static_url("components/codemirror/mode/%N/%N.js", include_version=False) }}";
297 297 </script>
298 298 <script src="{{ static_url("components/codemirror/addon/mode/loadmode.js") }}" charset="utf-8"></script>
299 299 <script src="{{ static_url("components/codemirror/addon/mode/multiplex.js") }}" charset="utf-8"></script>
300 300 <script src="{{ static_url("components/codemirror/addon/mode/overlay.js") }}" charset="utf-8"></script>
301 301 <script src="{{ static_url("components/codemirror/addon/edit/matchbrackets.js") }}" charset="utf-8"></script>
302 302 <script src="{{ static_url("components/codemirror/addon/edit/closebrackets.js") }}" charset="utf-8"></script>
303 303 <script src="{{ static_url("components/codemirror/addon/comment/comment.js") }}" charset="utf-8"></script>
304 304 <script src="{{ static_url("components/codemirror/mode/htmlmixed/htmlmixed.js") }}" charset="utf-8"></script>
305 305 <script src="{{ static_url("components/codemirror/mode/xml/xml.js") }}" charset="utf-8"></script>
306 306 <script src="{{ static_url("components/codemirror/mode/javascript/javascript.js") }}" charset="utf-8"></script>
307 307 <script src="{{ static_url("components/codemirror/mode/css/css.js") }}" charset="utf-8"></script>
308 308 <script src="{{ static_url("components/codemirror/mode/rst/rst.js") }}" charset="utf-8"></script>
309 309 <script src="{{ static_url("components/codemirror/mode/markdown/markdown.js") }}" charset="utf-8"></script>
310 310 <script src="{{ static_url("components/codemirror/mode/gfm/gfm.js") }}" charset="utf-8"></script>
311 311 <script src="{{ static_url("components/codemirror/mode/python/python.js") }}" charset="utf-8"></script>
312 312 <script src="{{ static_url("notebook/js/codemirror-ipython.js") }}" charset="utf-8"></script>
313 313
314 314 <script src="{{ static_url("components/highlight.js/build/highlight.pack.js") }}" charset="utf-8"></script>
315 315
316 316 <script src="{{ static_url("dateformat/date.format.js") }}" charset="utf-8"></script>
317 317
318 318 <script src="{{ static_url("base/js/events.js") }}" type="text/javascript" charset="utf-8"></script>
319 319 <script src="{{ static_url("base/js/utils.js") }}" type="text/javascript" charset="utf-8"></script>
320 320 <script src="{{ static_url("base/js/keyboard.js") }}" type="text/javascript" charset="utf-8"></script>
321 <script src="{{ static_url("base/js/security.js") }}" type="text/javascript" charset="utf-8"></script>
321 322 <script src="{{ static_url("base/js/dialog.js") }}" type="text/javascript" charset="utf-8"></script>
322 323 <script src="{{ static_url("services/kernels/js/kernel.js") }}" type="text/javascript" charset="utf-8"></script>
323 324 <script src="{{ static_url("services/kernels/js/comm.js") }}" type="text/javascript" charset="utf-8"></script>
324 325 <script src="{{ static_url("services/sessions/js/session.js") }}" type="text/javascript" charset="utf-8"></script>
325 326 <script src="{{ static_url("notebook/js/layoutmanager.js") }}" type="text/javascript" charset="utf-8"></script>
326 327 <script src="{{ static_url("notebook/js/mathjaxutils.js") }}" type="text/javascript" charset="utf-8"></script>
327 328 <script src="{{ static_url("notebook/js/outputarea.js") }}" type="text/javascript" charset="utf-8"></script>
328 329 <script src="{{ static_url("notebook/js/cell.js") }}" type="text/javascript" charset="utf-8"></script>
329 330 <script src="{{ static_url("notebook/js/celltoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
330 331 <script src="{{ static_url("notebook/js/codecell.js") }}" type="text/javascript" charset="utf-8"></script>
331 332 <script src="{{ static_url("notebook/js/completer.js") }}" type="text/javascript" charset="utf-8"></script>
332 333 <script src="{{ static_url("notebook/js/textcell.js") }}" type="text/javascript" charset="utf-8"></script>
333 334 <script src="{{ static_url("notebook/js/savewidget.js") }}" type="text/javascript" charset="utf-8"></script>
334 335 <script src="{{ static_url("notebook/js/quickhelp.js") }}" type="text/javascript" charset="utf-8"></script>
335 336 <script src="{{ static_url("notebook/js/pager.js") }}" type="text/javascript" charset="utf-8"></script>
336 337 <script src="{{ static_url("notebook/js/menubar.js") }}" type="text/javascript" charset="utf-8"></script>
337 338 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
338 339 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
339 340 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
340 341 <script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
341 342 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
342 343 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
343 344 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
344 345 <script src="{{ static_url("notebook/js/config.js") }}" type="text/javascript" charset="utf-8"></script>
345 346 <script src="{{ static_url("notebook/js/main.js") }}" type="text/javascript" charset="utf-8"></script>
346 347
347 348 <script src="{{ static_url("notebook/js/contexthint.js") }}" charset="utf-8"></script>
348 349
349 350 <script src="{{ static_url("notebook/js/celltoolbarpresets/default.js") }}" type="text/javascript" charset="utf-8"></script>
350 351 <script src="{{ static_url("notebook/js/celltoolbarpresets/rawcell.js") }}" type="text/javascript" charset="utf-8"></script>
351 352 <script src="{{ static_url("notebook/js/celltoolbarpresets/slideshow.js") }}" type="text/javascript" charset="utf-8"></script>
352 353
353 354 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now