##// END OF EJS Templates
Starting work on a Markdown cell.
Brian E. Granger -
Show More
@@ -0,0 +1,187 b''
1
2 //============================================================================
3 // RSTCell
4 //============================================================================
5
6 var IPython = (function (IPython) {
7
8 var RSTCell = function (notebook) {
9 IPython.Cell.apply(this, arguments);
10 this.placeholder = "Type ReStructured Text *here*."
11 this.rendered = false;
12 };
13
14
15 RSTCell.prototype = new IPython.Cell();
16
17
18
19 RSTCell.prototype.create_element = function () {
20 var cell = $("<div>").addClass('cell rst_cell border-box-sizing');
21 var input_area = $('<div/>').addClass('rst_cell_input');
22 this.code_mirror = CodeMirror(input_area.get(0), {
23 indentUnit : 4,
24 enterMode : 'flat',
25 tabMode: 'shift',
26 mode: 'rst',
27 theme: 'default',
28 value: this.placeholder
29 });
30 // The tabindex=-1 makes this div focusable.
31 var render_area = $('<div/>').addClass('rst_cell_render').
32 addClass('rendered_html').attr('tabindex','-1');
33 cell.append(input_area).append(render_area);
34 this.element = cell;
35 };
36
37
38 RSTCell.prototype.bind_events = function () {
39 IPython.Cell.prototype.bind_events.apply(this);
40 var that = this;
41 this.element.keydown(function (event) {
42 if (event.which === 13) {
43 if (that.rendered) {
44 that.edit();
45 event.preventDefault();
46 };
47 };
48 });
49 };
50
51
52 RSTCell.prototype.select = function () {
53 IPython.Cell.prototype.select.apply(this);
54 var output = this.element.find("div.rst_cell_render");
55 output.trigger('focus');
56 };
57
58
59 RSTCell.prototype.edit = function () {
60 if (this.rendered === true) {
61 var rst_cell = this.element;
62 var output = rst_cell.find("div.rst_cell_render");
63 output.hide();
64 rst_cell.find('div.rst_cell_input').show();
65 this.code_mirror.focus();
66 this.code_mirror.refresh();
67 this.rendered = false;
68 };
69 };
70
71
72 RSTCell.prototype.render = function () {
73 if (this.rendered === false) {
74 var text = this.get_source();
75 if (text === '') {text = this.placeholder;};
76 var html = IPython.markdown_converter.makeHtml(text);
77 this.set_rendered(html);
78 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
79 this.element.find('div.rst_cell_input').hide();
80 this.element.find("div.rst_cell_render").show();
81 this.rendered = true;
82 };
83 };
84
85
86 RSTCell.prototype.render_rst = function () {
87 if (this.rendered === false) {
88 var text = this.get_source();
89 if (text === "") {text = this.placeholder;};
90 var settings = {
91 processData : false,
92 cache : false,
93 type : "POST",
94 data : text,
95 headers : {'Content-Type': 'application/x-rst'},
96 success : $.proxy(this.handle_rendered,this)
97 };
98 $.ajax("/rstservice/render", settings);
99 };
100 };
101
102
103 RSTCell.prototype.handle_rendered = function (data, status, xhr) {
104 this.set_rendered(data);
105 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
106 this.element.find('div.rst_cell_input').hide();
107 this.element.find("div.rst_cell_render").show();
108 this.rendered = true;
109 };
110
111
112 RSTCell.prototype.config_mathjax = function () {
113 var rst_cell = this.element;
114 var that = this;
115 rst_cell.click(function () {
116 that.edit();
117 }).focusout(function () {
118 that.render();
119 });
120
121 rst_cell.trigger("focusout");
122 };
123
124
125 RSTCell.prototype.get_source = function () {
126 return this.code_mirror.getValue();
127 };
128
129
130 RSTCell.prototype.set_source = function (text) {
131 this.code_mirror.setValue(text);
132 this.code_mirror.refresh();
133 };
134
135
136 RSTCell.prototype.set_rendered = function (text) {
137 this.element.find('div.rst_cell_render').html(text);
138 };
139
140
141 RSTCell.prototype.get_rendered = function () {
142 return this.element.find('div.rst_cell_render').html();
143 };
144
145
146 RSTCell.prototype.at_top = function () {
147 if (this.rendered) {
148 return true;
149 } else {
150 return false;
151 }
152 };
153
154
155 RSTCell.prototype.at_bottom = function () {
156 if (this.rendered) {
157 return true;
158 } else {
159 return false;
160 }
161 };
162
163
164 RSTCell.prototype.fromJSON = function (data) {
165 if (data.cell_type === 'rst') {
166 if (data.source !== undefined) {
167 this.set_source(data.source);
168 this.set_rendered(data.rendered);
169 };
170 };
171 }
172
173
174 RSTCell.prototype.toJSON = function () {
175 var data = {}
176 data.cell_type = 'rst';
177 data.source = this.get_source();
178 data.rendered = this.get_rendered();
179 return data;
180 };
181
182 IPython.RSTCell = RSTCell;
183
184 return IPython;
185
186 }(IPython));
187
@@ -0,0 +1,32 b''
1 A javascript port of Markdown, as used on Stack Overflow
2 and the rest of Stack Exchange network.
3
4 Largely based on showdown.js by John Fraser (Attacklab).
5
6 Original Markdown Copyright (c) 2004-2005 John Gruber
7 <http://daringfireball.net/projects/markdown/>
8
9
10 Original Showdown code copyright (c) 2007 John Fraser
11
12 Modifications and bugfixes (c) 2009 Dana Robinson
13 Modifications and bugfixes (c) 2009-2011 Stack Exchange Inc.
14
15 Permission is hereby granted, free of charge, to any person obtaining a copy
16 of this software and associated documentation files (the "Software"), to deal
17 in the Software without restriction, including without limitation the rights
18 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19 copies of the Software, and to permit persons to whom the Software is
20 furnished to do so, subject to the following conditions:
21
22 The above copyright notice and this permission notice shall be included in
23 all copies or substantial portions of the Software.
24
25 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31 THE SOFTWARE.
32
This diff has been collapsed as it changes many lines, (1318 lines changed) Show them Hide them
@@ -0,0 +1,1318 b''
1 var Markdown;
2
3 if (typeof exports === "object" && typeof require === "function") // we're in a CommonJS (e.g. Node.js) module
4 Markdown = exports;
5 else
6 Markdown = {};
7
8 // The following text is included for historical reasons, but should
9 // be taken with a pinch of salt; it's not all true anymore.
10
11 //
12 // Wherever possible, Showdown is a straight, line-by-line port
13 // of the Perl version of Markdown.
14 //
15 // This is not a normal parser design; it's basically just a
16 // series of string substitutions. It's hard to read and
17 // maintain this way, but keeping Showdown close to the original
18 // design makes it easier to port new features.
19 //
20 // More importantly, Showdown behaves like markdown.pl in most
21 // edge cases. So web applications can do client-side preview
22 // in Javascript, and then build identical HTML on the server.
23 //
24 // This port needs the new RegExp functionality of ECMA 262,
25 // 3rd Edition (i.e. Javascript 1.5). Most modern web browsers
26 // should do fine. Even with the new regular expression features,
27 // We do a lot of work to emulate Perl's regex functionality.
28 // The tricky changes in this file mostly have the "attacklab:"
29 // label. Major or self-explanatory changes don't.
30 //
31 // Smart diff tools like Araxis Merge will be able to match up
32 // this file with markdown.pl in a useful way. A little tweaking
33 // helps: in a copy of markdown.pl, replace "#" with "//" and
34 // replace "$text" with "text". Be sure to ignore whitespace
35 // and line endings.
36 //
37
38
39 //
40 // Usage:
41 //
42 // var text = "Markdown *rocks*.";
43 //
44 // var converter = new Markdown.Converter();
45 // var html = converter.makeHtml(text);
46 //
47 // alert(html);
48 //
49 // Note: move the sample code to the bottom of this
50 // file before uncommenting it.
51 //
52
53 (function () {
54
55 function identity(x) { return x; }
56 function returnFalse(x) { return false; }
57
58 function HookCollection() { }
59
60 HookCollection.prototype = {
61
62 chain: function (hookname, func) {
63 var original = this[hookname];
64 if (!original)
65 throw new Error("unknown hook " + hookname);
66
67 if (original === identity)
68 this[hookname] = func;
69 else
70 this[hookname] = function (x) { return func(original(x)); }
71 },
72 set: function (hookname, func) {
73 if (!this[hookname])
74 throw new Error("unknown hook " + hookname);
75 this[hookname] = func;
76 },
77 addNoop: function (hookname) {
78 this[hookname] = identity;
79 },
80 addFalse: function (hookname) {
81 this[hookname] = returnFalse;
82 }
83 };
84
85 Markdown.HookCollection = HookCollection;
86
87 // g_urls and g_titles allow arbitrary user-entered strings as keys. This
88 // caused an exception (and hence stopped the rendering) when the user entered
89 // e.g. [push] or [__proto__]. Adding a prefix to the actual key prevents this
90 // (since no builtin property starts with "s_"). See
91 // http://meta.stackoverflow.com/questions/64655/strange-wmd-bug
92 // (granted, switching from Array() to Object() alone would have left only __proto__
93 // to be a problem)
94 function SaveHash() { }
95 SaveHash.prototype = {
96 set: function (key, value) {
97 this["s_" + key] = value;
98 },
99 get: function (key) {
100 return this["s_" + key];
101 }
102 };
103
104 Markdown.Converter = function () {
105 var pluginHooks = this.hooks = new HookCollection();
106 pluginHooks.addNoop("plainLinkText"); // given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link
107 pluginHooks.addNoop("preConversion"); // called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked
108 pluginHooks.addNoop("postConversion"); // called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml
109
110 //
111 // Private state of the converter instance:
112 //
113
114 // Global hashes, used by various utility routines
115 var g_urls;
116 var g_titles;
117 var g_html_blocks;
118
119 // Used to track when we're inside an ordered or unordered list
120 // (see _ProcessListItems() for details):
121 var g_list_level;
122
123 this.makeHtml = function (text) {
124
125 //
126 // Main function. The order in which other subs are called here is
127 // essential. Link and image substitutions need to happen before
128 // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the <a>
129 // and <img> tags get encoded.
130 //
131
132 // This will only happen if makeHtml on the same converter instance is called from a plugin hook.
133 // Don't do that.
134 if (g_urls)
135 throw new Error("Recursive call to converter.makeHtml");
136
137 // Create the private state objects.
138 g_urls = new SaveHash();
139 g_titles = new SaveHash();
140 g_html_blocks = [];
141 g_list_level = 0;
142
143 text = pluginHooks.preConversion(text);
144
145 // attacklab: Replace ~ with ~T
146 // This lets us use tilde as an escape char to avoid md5 hashes
147 // The choice of character is arbitray; anything that isn't
148 // magic in Markdown will work.
149 text = text.replace(/~/g, "~T");
150
151 // attacklab: Replace $ with ~D
152 // RegExp interprets $ as a special character
153 // when it's in a replacement string
154 text = text.replace(/\$/g, "~D");
155
156 // Standardize line endings
157 text = text.replace(/\r\n/g, "\n"); // DOS to Unix
158 text = text.replace(/\r/g, "\n"); // Mac to Unix
159
160 // Make sure text begins and ends with a couple of newlines:
161 text = "\n\n" + text + "\n\n";
162
163 // Convert all tabs to spaces.
164 text = _Detab(text);
165
166 // Strip any lines consisting only of spaces and tabs.
167 // This makes subsequent regexen easier to write, because we can
168 // match consecutive blank lines with /\n+/ instead of something
169 // contorted like /[ \t]*\n+/ .
170 text = text.replace(/^[ \t]+$/mg, "");
171
172 // Turn block-level HTML blocks into hash entries
173 text = _HashHTMLBlocks(text);
174
175 // Strip link definitions, store in hashes.
176 text = _StripLinkDefinitions(text);
177
178 text = _RunBlockGamut(text);
179
180 text = _UnescapeSpecialChars(text);
181
182 // attacklab: Restore dollar signs
183 text = text.replace(/~D/g, "$$");
184
185 // attacklab: Restore tildes
186 text = text.replace(/~T/g, "~");
187
188 text = pluginHooks.postConversion(text);
189
190 g_html_blocks = g_titles = g_urls = null;
191
192 return text;
193 };
194
195 function _StripLinkDefinitions(text) {
196 //
197 // Strips link definitions from text, stores the URLs and titles in
198 // hash references.
199 //
200
201 // Link defs are in the form: ^[id]: url "optional title"
202
203 /*
204 text = text.replace(/
205 ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1
206 [ \t]*
207 \n? // maybe *one* newline
208 [ \t]*
209 <?(\S+?)>? // url = $2
210 (?=\s|$) // lookahead for whitespace instead of the lookbehind removed below
211 [ \t]*
212 \n? // maybe one newline
213 [ \t]*
214 ( // (potential) title = $3
215 (\n*) // any lines skipped = $4 attacklab: lookbehind removed
216 [ \t]+
217 ["(]
218 (.+?) // title = $5
219 [")]
220 [ \t]*
221 )? // title is optional
222 (?:\n+|$)
223 /gm, function(){...});
224 */
225
226 text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm,
227 function (wholeMatch, m1, m2, m3, m4, m5) {
228 m1 = m1.toLowerCase();
229 g_urls.set(m1, _EncodeAmpsAndAngles(m2)); // Link IDs are case-insensitive
230 if (m4) {
231 // Oops, found blank lines, so it's not a title.
232 // Put back the parenthetical statement we stole.
233 return m3;
234 } else if (m5) {
235 g_titles.set(m1, m5.replace(/"/g, "&quot;"));
236 }
237
238 // Completely remove the definition from the text
239 return "";
240 }
241 );
242
243 return text;
244 }
245
246 function _HashHTMLBlocks(text) {
247
248 // Hashify HTML blocks:
249 // We only want to do this for block-level HTML tags, such as headers,
250 // lists, and tables. That's because we still want to wrap <p>s around
251 // "paragraphs" that are wrapped in non-block-level tags, such as anchors,
252 // phrase emphasis, and spans. The list of tags we're looking for is
253 // hard-coded:
254 var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"
255 var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"
256
257 // First, look for nested blocks, e.g.:
258 // <div>
259 // <div>
260 // tags for inner block must be indented.
261 // </div>
262 // </div>
263 //
264 // The outermost tags must start at the left margin for this to match, and
265 // the inner nested divs must be indented.
266 // We need to do this before the next, more liberal match, because the next
267 // match will start at the first `<div>` and stop at the first `</div>`.
268
269 // attacklab: This regex can be expensive when it fails.
270
271 /*
272 text = text.replace(/
273 ( // save in $1
274 ^ // start of line (with /m)
275 <($block_tags_a) // start tag = $2
276 \b // word break
277 // attacklab: hack around khtml/pcre bug...
278 [^\r]*?\n // any number of lines, minimally matching
279 </\2> // the matching end tag
280 [ \t]* // trailing spaces/tabs
281 (?=\n+) // followed by a newline
282 ) // attacklab: there are sentinel newlines at end of document
283 /gm,function(){...}};
284 */
285 text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, hashElement);
286
287 //
288 // Now match more liberally, simply from `\n<tag>` to `</tag>\n`
289 //
290
291 /*
292 text = text.replace(/
293 ( // save in $1
294 ^ // start of line (with /m)
295 <($block_tags_b) // start tag = $2
296 \b // word break
297 // attacklab: hack around khtml/pcre bug...
298 [^\r]*? // any number of lines, minimally matching
299 .*</\2> // the matching end tag
300 [ \t]* // trailing spaces/tabs
301 (?=\n+) // followed by a newline
302 ) // attacklab: there are sentinel newlines at end of document
303 /gm,function(){...}};
304 */
305 text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement);
306
307 // Special case just for <hr />. It was easier to make a special case than
308 // to make the other regex more complicated.
309
310 /*
311 text = text.replace(/
312 \n // Starting after a blank line
313 [ ]{0,3}
314 ( // save in $1
315 (<(hr) // start tag = $2
316 \b // word break
317 ([^<>])*?
318 \/?>) // the matching end tag
319 [ \t]*
320 (?=\n{2,}) // followed by a blank line
321 )
322 /g,hashElement);
323 */
324 text = text.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashElement);
325
326 // Special case for standalone HTML comments:
327
328 /*
329 text = text.replace(/
330 \n\n // Starting after a blank line
331 [ ]{0,3} // attacklab: g_tab_width - 1
332 ( // save in $1
333 <!
334 (--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--) // see http://www.w3.org/TR/html-markup/syntax.html#comments and http://meta.stackoverflow.com/q/95256
335 >
336 [ \t]*
337 (?=\n{2,}) // followed by a blank line
338 )
339 /g,hashElement);
340 */
341 text = text.replace(/\n\n[ ]{0,3}(<!(--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g, hashElement);
342
343 // PHP and ASP-style processor instructions (<?...?> and <%...%>)
344
345 /*
346 text = text.replace(/
347 (?:
348 \n\n // Starting after a blank line
349 )
350 ( // save in $1
351 [ ]{0,3} // attacklab: g_tab_width - 1
352 (?:
353 <([?%]) // $2
354 [^\r]*?
355 \2>
356 )
357 [ \t]*
358 (?=\n{2,}) // followed by a blank line
359 )
360 /g,hashElement);
361 */
362 text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashElement);
363
364 return text;
365 }
366
367 function hashElement(wholeMatch, m1) {
368 var blockText = m1;
369
370 // Undo double lines
371 blockText = blockText.replace(/^\n+/, "");
372
373 // strip trailing blank lines
374 blockText = blockText.replace(/\n+$/g, "");
375
376 // Replace the element text with a marker ("~KxK" where x is its key)
377 blockText = "\n\n~K" + (g_html_blocks.push(blockText) - 1) + "K\n\n";
378
379 return blockText;
380 }
381
382 function _RunBlockGamut(text, doNotUnhash) {
383 //
384 // These are all the transformations that form block-level
385 // tags like paragraphs, headers, and list items.
386 //
387 text = _DoHeaders(text);
388
389 // Do Horizontal Rules:
390 var replacement = "<hr />\n";
391 text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, replacement);
392 text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm, replacement);
393 text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, replacement);
394
395 text = _DoLists(text);
396 text = _DoCodeBlocks(text);
397 text = _DoBlockQuotes(text);
398
399 // We already ran _HashHTMLBlocks() before, in Markdown(), but that
400 // was to escape raw HTML in the original Markdown source. This time,
401 // we're escaping the markup we've just created, so that we don't wrap
402 // <p> tags around block-level tags.
403 text = _HashHTMLBlocks(text);
404 text = _FormParagraphs(text, doNotUnhash);
405
406 return text;
407 }
408
409 function _RunSpanGamut(text) {
410 //
411 // These are all the transformations that occur *within* block-level
412 // tags like paragraphs, headers, and list items.
413 //
414
415 text = _DoCodeSpans(text);
416 text = _EscapeSpecialCharsWithinTagAttributes(text);
417 text = _EncodeBackslashEscapes(text);
418
419 // Process anchor and image tags. Images must come first,
420 // because ![foo][f] looks like an anchor.
421 text = _DoImages(text);
422 text = _DoAnchors(text);
423
424 // Make links out of things like `<http://example.com/>`
425 // Must come after _DoAnchors(), because you can use < and >
426 // delimiters in inline links like [this](<url>).
427 text = _DoAutoLinks(text);
428 text = _EncodeAmpsAndAngles(text);
429 text = _DoItalicsAndBold(text);
430
431 // Do hard breaks:
432 text = text.replace(/ +\n/g, " <br>\n");
433
434 return text;
435 }
436
437 function _EscapeSpecialCharsWithinTagAttributes(text) {
438 //
439 // Within tags -- meaning between < and > -- encode [\ ` * _] so they
440 // don't conflict with their use in Markdown for code, italics and strong.
441 //
442
443 // Build a regex to find HTML tags and comments. See Friedl's
444 // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
445
446 // SE: changed the comment part of the regex
447
448 var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--(?:|(?:[^>-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi;
449
450 text = text.replace(regex, function (wholeMatch) {
451 var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`");
452 tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987
453 return tag;
454 });
455
456 return text;
457 }
458
459 function _DoAnchors(text) {
460 //
461 // Turn Markdown link shortcuts into XHTML <a> tags.
462 //
463 //
464 // First, handle reference-style links: [link text] [id]
465 //
466
467 /*
468 text = text.replace(/
469 ( // wrap whole match in $1
470 \[
471 (
472 (?:
473 \[[^\]]*\] // allow brackets nested one level
474 |
475 [^\[] // or anything else
476 )*
477 )
478 \]
479
480 [ ]? // one optional space
481 (?:\n[ ]*)? // one optional newline followed by spaces
482
483 \[
484 (.*?) // id = $3
485 \]
486 )
487 ()()()() // pad remaining backreferences
488 /g, writeAnchorTag);
489 */
490 text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag);
491
492 //
493 // Next, inline-style links: [link text](url "optional title")
494 //
495
496 /*
497 text = text.replace(/
498 ( // wrap whole match in $1
499 \[
500 (
501 (?:
502 \[[^\]]*\] // allow brackets nested one level
503 |
504 [^\[\]] // or anything else
505 )*
506 )
507 \]
508 \( // literal paren
509 [ \t]*
510 () // no id, so leave $3 empty
511 <?( // href = $4
512 (?:
513 \([^)]*\) // allow one level of (correctly nested) parens (think MSDN)
514 |
515 [^()]
516 )*?
517 )>?
518 [ \t]*
519 ( // $5
520 (['"]) // quote char = $6
521 (.*?) // Title = $7
522 \6 // matching quote
523 [ \t]* // ignore any spaces/tabs between closing quote and )
524 )? // title is optional
525 \)
526 )
527 /g, writeAnchorTag);
528 */
529
530 text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?((?:\([^)]*\)|[^()])*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag);
531
532 //
533 // Last, handle reference-style shortcuts: [link text]
534 // These must come last in case you've also got [link test][1]
535 // or [link test](/foo)
536 //
537
538 /*
539 text = text.replace(/
540 ( // wrap whole match in $1
541 \[
542 ([^\[\]]+) // link text = $2; can't contain '[' or ']'
543 \]
544 )
545 ()()()()() // pad rest of backreferences
546 /g, writeAnchorTag);
547 */
548 text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
549
550 return text;
551 }
552
553 function writeAnchorTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
554 if (m7 == undefined) m7 = "";
555 var whole_match = m1;
556 var link_text = m2;
557 var link_id = m3.toLowerCase();
558 var url = m4;
559 var title = m7;
560
561 if (url == "") {
562 if (link_id == "") {
563 // lower-case and turn embedded newlines into spaces
564 link_id = link_text.toLowerCase().replace(/ ?\n/g, " ");
565 }
566 url = "#" + link_id;
567
568 if (g_urls.get(link_id) != undefined) {
569 url = g_urls.get(link_id);
570 if (g_titles.get(link_id) != undefined) {
571 title = g_titles.get(link_id);
572 }
573 }
574 else {
575 if (whole_match.search(/\(\s*\)$/m) > -1) {
576 // Special case for explicit empty url
577 url = "";
578 } else {
579 return whole_match;
580 }
581 }
582 }
583 url = encodeProblemUrlChars(url);
584 url = escapeCharacters(url, "*_");
585 var result = "<a href=\"" + url + "\"";
586
587 if (title != "") {
588 title = title.replace(/"/g, "&quot;");
589 title = escapeCharacters(title, "*_");
590 result += " title=\"" + title + "\"";
591 }
592
593 result += ">" + link_text + "</a>";
594
595 return result;
596 }
597
598 function _DoImages(text) {
599 //
600 // Turn Markdown image shortcuts into <img> tags.
601 //
602
603 //
604 // First, handle reference-style labeled images: ![alt text][id]
605 //
606
607 /*
608 text = text.replace(/
609 ( // wrap whole match in $1
610 !\[
611 (.*?) // alt text = $2
612 \]
613
614 [ ]? // one optional space
615 (?:\n[ ]*)? // one optional newline followed by spaces
616
617 \[
618 (.*?) // id = $3
619 \]
620 )
621 ()()()() // pad rest of backreferences
622 /g, writeImageTag);
623 */
624 text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag);
625
626 //
627 // Next, handle inline images: ![alt text](url "optional title")
628 // Don't forget: encode * and _
629
630 /*
631 text = text.replace(/
632 ( // wrap whole match in $1
633 !\[
634 (.*?) // alt text = $2
635 \]
636 \s? // One optional whitespace character
637 \( // literal paren
638 [ \t]*
639 () // no id, so leave $3 empty
640 <?(\S+?)>? // src url = $4
641 [ \t]*
642 ( // $5
643 (['"]) // quote char = $6
644 (.*?) // title = $7
645 \6 // matching quote
646 [ \t]*
647 )? // title is optional
648 \)
649 )
650 /g, writeImageTag);
651 */
652 text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag);
653
654 return text;
655 }
656
657 function writeImageTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
658 var whole_match = m1;
659 var alt_text = m2;
660 var link_id = m3.toLowerCase();
661 var url = m4;
662 var title = m7;
663
664 if (!title) title = "";
665
666 if (url == "") {
667 if (link_id == "") {
668 // lower-case and turn embedded newlines into spaces
669 link_id = alt_text.toLowerCase().replace(/ ?\n/g, " ");
670 }
671 url = "#" + link_id;
672
673 if (g_urls.get(link_id) != undefined) {
674 url = g_urls.get(link_id);
675 if (g_titles.get(link_id) != undefined) {
676 title = g_titles.get(link_id);
677 }
678 }
679 else {
680 return whole_match;
681 }
682 }
683
684 alt_text = alt_text.replace(/"/g, "&quot;");
685 url = escapeCharacters(url, "*_");
686 var result = "<img src=\"" + url + "\" alt=\"" + alt_text + "\"";
687
688 // attacklab: Markdown.pl adds empty title attributes to images.
689 // Replicate this bug.
690
691 //if (title != "") {
692 title = title.replace(/"/g, "&quot;");
693 title = escapeCharacters(title, "*_");
694 result += " title=\"" + title + "\"";
695 //}
696
697 result += " />";
698
699 return result;
700 }
701
702 function _DoHeaders(text) {
703
704 // Setext-style headers:
705 // Header 1
706 // ========
707 //
708 // Header 2
709 // --------
710 //
711 text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
712 function (wholeMatch, m1) { return "<h1>" + _RunSpanGamut(m1) + "</h1>\n\n"; }
713 );
714
715 text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
716 function (matchFound, m1) { return "<h2>" + _RunSpanGamut(m1) + "</h2>\n\n"; }
717 );
718
719 // atx-style headers:
720 // # Header 1
721 // ## Header 2
722 // ## Header 2 with closing hashes ##
723 // ...
724 // ###### Header 6
725 //
726
727 /*
728 text = text.replace(/
729 ^(\#{1,6}) // $1 = string of #'s
730 [ \t]*
731 (.+?) // $2 = Header text
732 [ \t]*
733 \#* // optional closing #'s (not counted)
734 \n+
735 /gm, function() {...});
736 */
737
738 text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
739 function (wholeMatch, m1, m2) {
740 var h_level = m1.length;
741 return "<h" + h_level + ">" + _RunSpanGamut(m2) + "</h" + h_level + ">\n\n";
742 }
743 );
744
745 return text;
746 }
747
748 function _DoLists(text) {
749 //
750 // Form HTML ordered (numbered) and unordered (bulleted) lists.
751 //
752
753 // attacklab: add sentinel to hack around khtml/safari bug:
754 // http://bugs.webkit.org/show_bug.cgi?id=11231
755 text += "~0";
756
757 // Re-usable pattern to match any entirel ul or ol list:
758
759 /*
760 var whole_list = /
761 ( // $1 = whole list
762 ( // $2
763 [ ]{0,3} // attacklab: g_tab_width - 1
764 ([*+-]|\d+[.]) // $3 = first list item marker
765 [ \t]+
766 )
767 [^\r]+?
768 ( // $4
769 ~0 // sentinel for workaround; should be $
770 |
771 \n{2,}
772 (?=\S)
773 (?! // Negative lookahead for another list item marker
774 [ \t]*
775 (?:[*+-]|\d+[.])[ \t]+
776 )
777 )
778 )
779 /g
780 */
781 var whole_list = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
782
783 if (g_list_level) {
784 text = text.replace(whole_list, function (wholeMatch, m1, m2) {
785 var list = m1;
786 var list_type = (m2.search(/[*+-]/g) > -1) ? "ul" : "ol";
787
788 var result = _ProcessListItems(list, list_type);
789
790 // Trim any trailing whitespace, to put the closing `</$list_type>`
791 // up on the preceding line, to get it past the current stupid
792 // HTML block parser. This is a hack to work around the terrible
793 // hack that is the HTML block parser.
794 result = result.replace(/\s+$/, "");
795 result = "<" + list_type + ">" + result + "</" + list_type + ">\n";
796 return result;
797 });
798 } else {
799 whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
800 text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) {
801 var runup = m1;
802 var list = m2;
803
804 var list_type = (m3.search(/[*+-]/g) > -1) ? "ul" : "ol";
805 var result = _ProcessListItems(list, list_type);
806 result = runup + "<" + list_type + ">\n" + result + "</" + list_type + ">\n";
807 return result;
808 });
809 }
810
811 // attacklab: strip sentinel
812 text = text.replace(/~0/, "");
813
814 return text;
815 }
816
817 var _listItemMarkers = { ol: "\\d+[.]", ul: "[*+-]" };
818
819 function _ProcessListItems(list_str, list_type) {
820 //
821 // Process the contents of a single ordered or unordered list, splitting it
822 // into individual list items.
823 //
824 // list_type is either "ul" or "ol".
825
826 // The $g_list_level global keeps track of when we're inside a list.
827 // Each time we enter a list, we increment it; when we leave a list,
828 // we decrement. If it's zero, we're not in a list anymore.
829 //
830 // We do this because when we're not inside a list, we want to treat
831 // something like this:
832 //
833 // I recommend upgrading to version
834 // 8. Oops, now this line is treated
835 // as a sub-list.
836 //
837 // As a single paragraph, despite the fact that the second line starts
838 // with a digit-period-space sequence.
839 //
840 // Whereas when we're inside a list (or sub-list), that line will be
841 // treated as the start of a sub-list. What a kludge, huh? This is
842 // an aspect of Markdown's syntax that's hard to parse perfectly
843 // without resorting to mind-reading. Perhaps the solution is to
844 // change the syntax rules such that sub-lists must start with a
845 // starting cardinal number; e.g. "1." or "a.".
846
847 g_list_level++;
848
849 // trim trailing blank lines:
850 list_str = list_str.replace(/\n{2,}$/, "\n");
851
852 // attacklab: add sentinel to emulate \z
853 list_str += "~0";
854
855 // In the original attacklab showdown, list_type was not given to this function, and anything
856 // that matched /[*+-]|\d+[.]/ would just create the next <li>, causing this mismatch:
857 //
858 // Markdown rendered by WMD rendered by MarkdownSharp
859 // ------------------------------------------------------------------
860 // 1. first 1. first 1. first
861 // 2. second 2. second 2. second
862 // - third 3. third * third
863 //
864 // We changed this to behave identical to MarkdownSharp. This is the constructed RegEx,
865 // with {MARKER} being one of \d+[.] or [*+-], depending on list_type:
866
867 /*
868 list_str = list_str.replace(/
869 (^[ \t]*) // leading whitespace = $1
870 ({MARKER}) [ \t]+ // list marker = $2
871 ([^\r]+? // list item text = $3
872 (\n+)
873 )
874 (?=
875 (~0 | \2 ({MARKER}) [ \t]+)
876 )
877 /gm, function(){...});
878 */
879
880 var marker = _listItemMarkers[list_type];
881 var re = new RegExp("(^[ \\t]*)(" + marker + ")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1(" + marker + ")[ \\t]+))", "gm");
882 var last_item_had_a_double_newline = false;
883 list_str = list_str.replace(re,
884 function (wholeMatch, m1, m2, m3) {
885 var item = m3;
886 var leading_space = m1;
887 var ends_with_double_newline = /\n\n$/.test(item);
888 var contains_double_newline = ends_with_double_newline || item.search(/\n{2,}/) > -1;
889
890 if (contains_double_newline || last_item_had_a_double_newline) {
891 item = _RunBlockGamut(_Outdent(item), /* doNotUnhash = */true);
892 }
893 else {
894 // Recursion for sub-lists:
895 item = _DoLists(_Outdent(item));
896 item = item.replace(/\n$/, ""); // chomp(item)
897 item = _RunSpanGamut(item);
898 }
899 last_item_had_a_double_newline = ends_with_double_newline;
900 return "<li>" + item + "</li>\n";
901 }
902 );
903
904 // attacklab: strip sentinel
905 list_str = list_str.replace(/~0/g, "");
906
907 g_list_level--;
908 return list_str;
909 }
910
911 function _DoCodeBlocks(text) {
912 //
913 // Process Markdown `<pre><code>` blocks.
914 //
915
916 /*
917 text = text.replace(/
918 (?:\n\n|^)
919 ( // $1 = the code block -- one or more lines, starting with a space/tab
920 (?:
921 (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
922 .*\n+
923 )+
924 )
925 (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
926 /g ,function(){...});
927 */
928
929 // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
930 text += "~0";
931
932 text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
933 function (wholeMatch, m1, m2) {
934 var codeblock = m1;
935 var nextChar = m2;
936
937 codeblock = _EncodeCode(_Outdent(codeblock));
938 codeblock = _Detab(codeblock);
939 codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
940 codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
941
942 codeblock = "<pre><code>" + codeblock + "\n</code></pre>";
943
944 return "\n\n" + codeblock + "\n\n" + nextChar;
945 }
946 );
947
948 // attacklab: strip sentinel
949 text = text.replace(/~0/, "");
950
951 return text;
952 }
953
954 function hashBlock(text) {
955 text = text.replace(/(^\n+|\n+$)/g, "");
956 return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n";
957 }
958
959 function _DoCodeSpans(text) {
960 //
961 // * Backtick quotes are used for <code></code> spans.
962 //
963 // * You can use multiple backticks as the delimiters if you want to
964 // include literal backticks in the code span. So, this input:
965 //
966 // Just type ``foo `bar` baz`` at the prompt.
967 //
968 // Will translate to:
969 //
970 // <p>Just type <code>foo `bar` baz</code> at the prompt.</p>
971 //
972 // There's no arbitrary limit to the number of backticks you
973 // can use as delimters. If you need three consecutive backticks
974 // in your code, use four for delimiters, etc.
975 //
976 // * You can use spaces to get literal backticks at the edges:
977 //
978 // ... type `` `bar` `` ...
979 //
980 // Turns to:
981 //
982 // ... type <code>`bar`</code> ...
983 //
984
985 /*
986 text = text.replace(/
987 (^|[^\\]) // Character before opening ` can't be a backslash
988 (`+) // $2 = Opening run of `
989 ( // $3 = The code block
990 [^\r]*?
991 [^`] // attacklab: work around lack of lookbehind
992 )
993 \2 // Matching closer
994 (?!`)
995 /gm, function(){...});
996 */
997
998 text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
999 function (wholeMatch, m1, m2, m3, m4) {
1000 var c = m3;
1001 c = c.replace(/^([ \t]*)/g, ""); // leading whitespace
1002 c = c.replace(/[ \t]*$/g, ""); // trailing whitespace
1003 c = _EncodeCode(c);
1004 return m1 + "<code>" + c + "</code>";
1005 }
1006 );
1007
1008 return text;
1009 }
1010
1011 function _EncodeCode(text) {
1012 //
1013 // Encode/escape certain characters inside Markdown code runs.
1014 // The point is that in code, these characters are literals,
1015 // and lose their special Markdown meanings.
1016 //
1017 // Encode all ampersands; HTML entities are not
1018 // entities within a Markdown code span.
1019 text = text.replace(/&/g, "&amp;");
1020
1021 // Do the angle bracket song and dance:
1022 text = text.replace(/</g, "&lt;");
1023 text = text.replace(/>/g, "&gt;");
1024
1025 // Now, escape characters that are magic in Markdown:
1026 text = escapeCharacters(text, "\*_{}[]\\", false);
1027
1028 // jj the line above breaks this:
1029 //---
1030
1031 //* Item
1032
1033 // 1. Subitem
1034
1035 // special char: *
1036 //---
1037
1038 return text;
1039 }
1040
1041 function _DoItalicsAndBold(text) {
1042
1043 // <strong> must go first:
1044 text = text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g,
1045 "$1<strong>$3</strong>$4");
1046
1047 text = text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g,
1048 "$1<em>$3</em>$4");
1049
1050 return text;
1051 }
1052
1053 function _DoBlockQuotes(text) {
1054
1055 /*
1056 text = text.replace(/
1057 ( // Wrap whole match in $1
1058 (
1059 ^[ \t]*>[ \t]? // '>' at the start of a line
1060 .+\n // rest of the first line
1061 (.+\n)* // subsequent consecutive lines
1062 \n* // blanks
1063 )+
1064 )
1065 /gm, function(){...});
1066 */
1067
1068 text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1069 function (wholeMatch, m1) {
1070 var bq = m1;
1071
1072 // attacklab: hack around Konqueror 3.5.4 bug:
1073 // "----------bug".replace(/^-/g,"") == "bug"
1074
1075 bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting
1076
1077 // attacklab: clean up hack
1078 bq = bq.replace(/~0/g, "");
1079
1080 bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines
1081 bq = _RunBlockGamut(bq); // recurse
1082
1083 bq = bq.replace(/(^|\n)/g, "$1 ");
1084 // These leading spaces screw with <pre> content, so we need to fix that:
1085 bq = bq.replace(
1086 /(\s*<pre>[^\r]+?<\/pre>)/gm,
1087 function (wholeMatch, m1) {
1088 var pre = m1;
1089 // attacklab: hack around Konqueror 3.5.4 bug:
1090 pre = pre.replace(/^ /mg, "~0");
1091 pre = pre.replace(/~0/g, "");
1092 return pre;
1093 });
1094
1095 return hashBlock("<blockquote>\n" + bq + "\n</blockquote>");
1096 }
1097 );
1098 return text;
1099 }
1100
1101 function _FormParagraphs(text, doNotUnhash) {
1102 //
1103 // Params:
1104 // $text - string to process with html <p> tags
1105 //
1106
1107 // Strip leading and trailing lines:
1108 text = text.replace(/^\n+/g, "");
1109 text = text.replace(/\n+$/g, "");
1110
1111 var grafs = text.split(/\n{2,}/g);
1112 var grafsOut = [];
1113
1114 //
1115 // Wrap <p> tags.
1116 //
1117 var end = grafs.length;
1118 for (var i = 0; i < end; i++) {
1119 var str = grafs[i];
1120
1121 // if this is an HTML marker, copy it
1122 if (str.search(/~K(\d+)K/g) >= 0) {
1123 grafsOut.push(str);
1124 }
1125 else if (str.search(/\S/) >= 0) {
1126 str = _RunSpanGamut(str);
1127 str = str.replace(/^([ \t]*)/g, "<p>");
1128 str += "</p>"
1129 grafsOut.push(str);
1130 }
1131
1132 }
1133 //
1134 // Unhashify HTML blocks
1135 //
1136 if (!doNotUnhash) {
1137 end = grafsOut.length;
1138 for (var i = 0; i < end; i++) {
1139 // if this is a marker for an html block...
1140 while (grafsOut[i].search(/~K(\d+)K/) >= 0) {
1141 var blockText = g_html_blocks[RegExp.$1];
1142 blockText = blockText.replace(/\$/g, "$$$$"); // Escape any dollar signs
1143 grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText);
1144 }
1145 }
1146 }
1147 return grafsOut.join("\n\n");
1148 }
1149
1150 function _EncodeAmpsAndAngles(text) {
1151 // Smart processing for ampersands and angle brackets that need to be encoded.
1152
1153 // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:
1154 // http://bumppo.net/projects/amputator/
1155 text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&amp;");
1156
1157 // Encode naked <'s
1158 text = text.replace(/<(?![a-z\/?\$!])/gi, "&lt;");
1159
1160 return text;
1161 }
1162
1163 function _EncodeBackslashEscapes(text) {
1164 //
1165 // Parameter: String.
1166 // Returns: The string, with after processing the following backslash
1167 // escape sequences.
1168 //
1169
1170 // attacklab: The polite way to do this is with the new
1171 // escapeCharacters() function:
1172 //
1173 // text = escapeCharacters(text,"\\",true);
1174 // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true);
1175 //
1176 // ...but we're sidestepping its use of the (slow) RegExp constructor
1177 // as an optimization for Firefox. This function gets called a LOT.
1178
1179 text = text.replace(/\\(\\)/g, escapeCharacters_callback);
1180 text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, escapeCharacters_callback);
1181 return text;
1182 }
1183
1184 function _DoAutoLinks(text) {
1185
1186 // note that at this point, all other URL in the text are already hyperlinked as <a href=""></a>
1187 // *except* for the <http://www.foo.com> case
1188
1189 // automatically add < and > around unadorned raw hyperlinks
1190 // must be preceded by space/BOF and followed by non-word/EOF character
1191 text = text.replace(/(^|\s)(https?|ftp)(:\/\/[-A-Z0-9+&@#\/%?=~_|\[\]\(\)!:,\.;]*[-A-Z0-9+&@#\/%=~_|\[\]])($|\W)/gi, "$1<$2$3>$4");
1192
1193 // autolink anything like <http://example.com>
1194
1195 var replacer = function (wholematch, m1) { return "<a href=\"" + m1 + "\">" + pluginHooks.plainLinkText(m1) + "</a>"; }
1196 text = text.replace(/<((https?|ftp):[^'">\s]+)>/gi, replacer);
1197
1198 // Email addresses: <address@domain.foo>
1199 /*
1200 text = text.replace(/
1201 <
1202 (?:mailto:)?
1203 (
1204 [-.\w]+
1205 \@
1206 [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+
1207 )
1208 >
1209 /gi, _DoAutoLinks_callback());
1210 */
1211
1212 /* disabling email autolinking, since we don't do that on the server, either
1213 text = text.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,
1214 function(wholeMatch,m1) {
1215 return _EncodeEmailAddress( _UnescapeSpecialChars(m1) );
1216 }
1217 );
1218 */
1219 return text;
1220 }
1221
1222 function _UnescapeSpecialChars(text) {
1223 //
1224 // Swap back in all the special characters we've hidden.
1225 //
1226 text = text.replace(/~E(\d+)E/g,
1227 function (wholeMatch, m1) {
1228 var charCodeToReplace = parseInt(m1);
1229 return String.fromCharCode(charCodeToReplace);
1230 }
1231 );
1232 return text;
1233 }
1234
1235 function _Outdent(text) {
1236 //
1237 // Remove one level of line-leading tabs or spaces
1238 //
1239
1240 // attacklab: hack around Konqueror 3.5.4 bug:
1241 // "----------bug".replace(/^-/g,"") == "bug"
1242
1243 text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width
1244
1245 // attacklab: clean up hack
1246 text = text.replace(/~0/g, "")
1247
1248 return text;
1249 }
1250
1251 function _Detab(text) {
1252 if (!/\t/.test(text))
1253 return text;
1254
1255 var spaces = [" ", " ", " ", " "],
1256 skew = 0,
1257 v;
1258
1259 return text.replace(/[\n\t]/g, function (match, offset) {
1260 if (match === "\n") {
1261 skew = offset + 1;
1262 return match;
1263 }
1264 v = (offset - skew) % 4;
1265 skew = offset + 1;
1266 return spaces[v];
1267 });
1268 }
1269
1270 //
1271 // attacklab: Utility functions
1272 //
1273
1274 var _problemUrlChars = /(?:["'*()[\]:]|~D)/g;
1275
1276 // hex-encodes some unusual "problem" chars in URLs to avoid URL detection problems
1277 function encodeProblemUrlChars(url) {
1278 if (!url)
1279 return "";
1280
1281 var len = url.length;
1282
1283 return url.replace(_problemUrlChars, function (match, offset) {
1284 if (match == "~D") // escape for dollar
1285 return "%24";
1286 if (match == ":") {
1287 if (offset == len - 1 || /[0-9\/]/.test(url.charAt(offset + 1)))
1288 return ":"
1289 }
1290 return "%" + match.charCodeAt(0).toString(16);
1291 });
1292 }
1293
1294
1295 function escapeCharacters(text, charsToEscape, afterBackslash) {
1296 // First we have to escape the escape characters so that
1297 // we can build a character class out of them
1298 var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g, "\\$1") + "])";
1299
1300 if (afterBackslash) {
1301 regexString = "\\\\" + regexString;
1302 }
1303
1304 var regex = new RegExp(regexString, "g");
1305 text = text.replace(regex, escapeCharacters_callback);
1306
1307 return text;
1308 }
1309
1310
1311 function escapeCharacters_callback(wholeMatch, m1) {
1312 var charCodeToEscape = m1.charCodeAt(0);
1313 return "~E" + charCodeToEscape + "E";
1314 }
1315
1316 }; // end of the Markdown.Converter constructor
1317
1318 })();
@@ -1,158 +1,212 b''
1 1 """Tornado handlers for the notebook."""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Imports
5 5 #-----------------------------------------------------------------------------
6 6
7 7 import json
8 8 import logging
9 import os
9 10 import urllib
10 11
11 12 from tornado import web
12 13 from tornado import websocket
13 14
15 try:
16 from docutils.core import publish_string
17 except ImportError:
18 publish_string = None
19
14 20
15 21 #-----------------------------------------------------------------------------
16 22 # Top-level handlers
17 23 #-----------------------------------------------------------------------------
18 24
19 25
20 26 class NBBrowserHandler(web.RequestHandler):
21 27 def get(self):
22 28 nbm = self.application.notebook_manager
23 29 project = nbm.notebook_dir
24 30 self.render('nbbrowser.html', project=project)
25 31
26 32
27 33 class NewHandler(web.RequestHandler):
28 34 def get(self):
29 35 notebook_id = self.application.notebook_manager.new_notebook()
30 36 self.render('notebook.html', notebook_id=notebook_id)
31 37
32 38
33 39 class NamedNotebookHandler(web.RequestHandler):
34 40 def get(self, notebook_id):
35 41 nbm = self.application.notebook_manager
36 42 if not nbm.notebook_exists(notebook_id):
37 43 raise web.HTTPError(404)
38 44 self.render('notebook.html', notebook_id=notebook_id)
39 45
40 46
41 47 #-----------------------------------------------------------------------------
42 48 # Kernel handlers
43 49 #-----------------------------------------------------------------------------
44 50
45 51
46 52 class MainKernelHandler(web.RequestHandler):
47 53
48 54 def get(self):
49 55 rkm = self.application.routing_kernel_manager
50 56 self.finish(json.dumps(rkm.kernel_ids))
51 57
52 58 def post(self):
53 59 rkm = self.application.routing_kernel_manager
54 60 notebook_id = self.get_argument('notebook', default=None)
55 61 kernel_id = rkm.start_kernel(notebook_id)
56 62 self.set_header('Location', '/'+kernel_id)
57 63 self.finish(json.dumps(kernel_id))
58 64
59 65
60 66 class KernelHandler(web.RequestHandler):
61 67
62 68 SUPPORTED_METHODS = ('DELETE')
63 69
64 70 def delete(self, kernel_id):
65 71 rkm = self.application.routing_kernel_manager
66 72 rkm.kill_kernel(kernel_id)
67 73 self.set_status(204)
68 74 self.finish()
69 75
70 76
71 77 class KernelActionHandler(web.RequestHandler):
72 78
73 79 def post(self, kernel_id, action):
74 80 rkm = self.application.routing_kernel_manager
75 81 if action == 'interrupt':
76 82 rkm.interrupt_kernel(kernel_id)
77 83 self.set_status(204)
78 84 if action == 'restart':
79 85 new_kernel_id = rkm.restart_kernel(kernel_id)
80 86 self.write(json.dumps(new_kernel_id))
81 87 self.finish()
82 88
83 89
84 90 class ZMQStreamHandler(websocket.WebSocketHandler):
85 91
86 92 def initialize(self, stream_name):
87 93 self.stream_name = stream_name
88 94
89 95 def open(self, kernel_id):
90 96 rkm = self.application.routing_kernel_manager
91 97 self.router = rkm.get_router(kernel_id, self.stream_name)
92 98 self.client_id = self.router.register_client(self)
93 99
94 100 def on_message(self, msg):
95 101 self.router.forward_msg(self.client_id, msg)
96 102
97 103 def on_close(self):
98 104 self.router.unregister_client(self.client_id)
99 105
100 106
101 107 #-----------------------------------------------------------------------------
102 108 # Notebook web service handlers
103 109 #-----------------------------------------------------------------------------
104 110
105 111 class NotebookRootHandler(web.RequestHandler):
106 112
107 113 def get(self):
108 114 nbm = self.application.notebook_manager
109 115 files = nbm.list_notebooks()
110 116 self.finish(json.dumps(files))
111 117
112 118 def post(self):
113 119 nbm = self.application.notebook_manager
114 120 body = self.request.body.strip()
115 121 format = self.get_argument('format', default='json')
116 122 name = self.get_argument('name', default=None)
117 123 if body:
118 124 notebook_id = nbm.save_new_notebook(body, name=name, format=format)
119 125 else:
120 126 notebook_id = nbm.new_notebook()
121 127 self.set_header('Location', '/'+notebook_id)
122 128 self.finish(json.dumps(notebook_id))
123 129
124 130
125 131 class NotebookHandler(web.RequestHandler):
126 132
127 133 SUPPORTED_METHODS = ('GET', 'PUT', 'DELETE')
128 134
129 135 def get(self, notebook_id):
130 136 nbm = self.application.notebook_manager
131 137 format = self.get_argument('format', default='json')
132 138 last_mod, name, data = nbm.get_notebook(notebook_id, format)
133 139 if format == u'json':
134 140 self.set_header('Content-Type', 'application/json')
135 141 self.set_header('Content-Disposition','attachment; filename=%s.json' % name)
136 142 elif format == u'xml':
137 143 self.set_header('Content-Type', 'application/xml')
138 144 self.set_header('Content-Disposition','attachment; filename=%s.ipynb' % name)
139 145 elif format == u'py':
140 146 self.set_header('Content-Type', 'application/x-python')
141 147 self.set_header('Content-Disposition','attachment; filename=%s.py' % name)
142 148 self.set_header('Last-Modified', last_mod)
143 149 self.finish(data)
144 150
145 151 def put(self, notebook_id):
146 152 nbm = self.application.notebook_manager
147 153 format = self.get_argument('format', default='json')
148 154 name = self.get_argument('name', default=None)
149 155 nbm.save_notebook(notebook_id, self.request.body, name=name, format=format)
150 156 self.set_status(204)
151 157 self.finish()
152 158
153 159 def delete(self, notebook_id):
154 160 nbm = self.application.notebook_manager
155 161 nbm.delete_notebook(notebook_id)
156 162 self.set_status(204)
157 163 self.finish()
158 164
165 #-----------------------------------------------------------------------------
166 # RST web service handlers
167 #-----------------------------------------------------------------------------
168
169 _rst_header = """========
170 Heading1
171 ========
172
173 Heading2
174 ========
175
176 Heading3
177 --------
178
179 Heading4
180 ^^^^^^^^
181
182 """
183
184 class RSTHandler(web.RequestHandler):
185
186 def post(self):
187 if publish_string is None:
188 raise web.HTTPError(503)
189 body = self.request.body.strip()
190 source = _rst_header + body
191 template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html')
192 print template_path
193 defaults = {'file_insertion_enabled': 0,
194 'raw_enabled': 0,
195 '_disable_config': 1,
196 'stylesheet_path': 0,
197 'initial_header_level': 3,
198 'template': template_path
199 }
200 try:
201 html = publish_string(source, writer_name='html',
202 settings_overrides=defaults
203 )
204 except:
205 raise web.HTTPError(400)
206 print html
207 # html = '\n'.join(html.split('\n')[7:-3])
208 # print html
209 self.set_header('Content-Type', 'text/html')
210 self.finish(html)
211
212
@@ -1,203 +1,204 b''
1 1 """A tornado based IPython notebook server."""
2 2
3 3 #-----------------------------------------------------------------------------
4 4 # Copyright (C) 2011 The IPython Development Team
5 5 #
6 6 # Distributed under the terms of the BSD License. The full license is in
7 7 # the file COPYING.txt, distributed as part of this software.
8 8 #-----------------------------------------------------------------------------
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Imports
12 12 #-----------------------------------------------------------------------------
13 13
14 14 import logging
15 15 import os
16 16 import signal
17 17 import sys
18 18
19 19 import zmq
20 20
21 21 # Install the pyzmq ioloop. This has to be done before anything else from
22 22 # tornado is imported.
23 23 from zmq.eventloop import ioloop
24 24 import tornado.ioloop
25 25 tornado.ioloop = ioloop
26 26
27 27 from tornado import httpserver
28 28 from tornado import web
29 29
30 30 from .kernelmanager import KernelManager, RoutingKernelManager
31 31 from .sessionmanager import SessionManager
32 32 from .handlers import (
33 33 NBBrowserHandler, NewHandler, NamedNotebookHandler,
34 34 MainKernelHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler,
35 NotebookRootHandler, NotebookHandler
35 NotebookRootHandler, NotebookHandler, RSTHandler
36 36 )
37 37 from .notebookmanager import NotebookManager
38 38
39 39 from IPython.core.application import BaseIPythonApplication
40 40 from IPython.core.profiledir import ProfileDir
41 41 from IPython.frontend.qt.console.rich_ipython_widget import RichIPythonWidget
42 42 from IPython.zmq.session import Session
43 43 from IPython.zmq.zmqshell import ZMQInteractiveShell
44 44 from IPython.zmq.ipkernel import (
45 45 flags as ipkernel_flags,
46 46 aliases as ipkernel_aliases,
47 47 IPKernelApp
48 48 )
49 49 from IPython.utils.traitlets import Dict, Unicode, Int, Any, List, Enum
50 50
51 51 #-----------------------------------------------------------------------------
52 52 # Module globals
53 53 #-----------------------------------------------------------------------------
54 54
55 55 _kernel_id_regex = r"(?P<kernel_id>\w+-\w+-\w+-\w+-\w+)"
56 56 _kernel_action_regex = r"(?P<action>restart|interrupt)"
57 57 _notebook_id_regex = r"(?P<notebook_id>\w+-\w+-\w+-\w+-\w+)"
58 58
59 59 LOCALHOST = '127.0.0.1'
60 60
61 61 #-----------------------------------------------------------------------------
62 62 # The Tornado web application
63 63 #-----------------------------------------------------------------------------
64 64
65 65 class NotebookWebApplication(web.Application):
66 66
67 67 def __init__(self, routing_kernel_manager, notebook_manager, log):
68 68 handlers = [
69 69 (r"/", NBBrowserHandler),
70 70 (r"/new", NewHandler),
71 71 (r"/%s" % _notebook_id_regex, NamedNotebookHandler),
72 72 (r"/kernels", MainKernelHandler),
73 73 (r"/kernels/%s" % _kernel_id_regex, KernelHandler),
74 74 (r"/kernels/%s/%s" % (_kernel_id_regex, _kernel_action_regex), KernelActionHandler),
75 75 (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')),
76 76 (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')),
77 77 (r"/notebooks", NotebookRootHandler),
78 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler)
78 (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler),
79 (r"/rstservice/render", RSTHandler)
79 80 ]
80 81 settings = dict(
81 82 template_path=os.path.join(os.path.dirname(__file__), "templates"),
82 83 static_path=os.path.join(os.path.dirname(__file__), "static"),
83 84 )
84 85 web.Application.__init__(self, handlers, **settings)
85 86
86 87 self.routing_kernel_manager = routing_kernel_manager
87 88 self.log = log
88 89 self.notebook_manager = notebook_manager
89 90
90 91
91 92 #-----------------------------------------------------------------------------
92 93 # Aliases and Flags
93 94 #-----------------------------------------------------------------------------
94 95
95 96 flags = dict(ipkernel_flags)
96 97
97 98 # the flags that are specific to the frontend
98 99 # these must be scrubbed before being passed to the kernel,
99 100 # or it will raise an error on unrecognized flags
100 101 notebook_flags = []
101 102
102 103 aliases = dict(ipkernel_aliases)
103 104
104 105 aliases.update(dict(
105 106 ip = 'IPythonNotebookApp.ip',
106 107 port = 'IPythonNotebookApp.port',
107 108 colors = 'ZMQInteractiveShell.colors',
108 109 editor = 'RichIPythonWidget.editor',
109 110 ))
110 111
111 112 #-----------------------------------------------------------------------------
112 113 # IPythonNotebookApp
113 114 #-----------------------------------------------------------------------------
114 115
115 116 class IPythonNotebookApp(BaseIPythonApplication):
116 117 name = 'ipython-notebook'
117 118 default_config_file_name='ipython_notebook_config.py'
118 119
119 120 description = """
120 121 The IPython HTML Notebook.
121 122
122 123 This launches a Tornado based HTML Notebook Server that serves up an
123 124 HTML5/Javascript Notebook client.
124 125 """
125 126
126 127 classes = [IPKernelApp, ZMQInteractiveShell, ProfileDir, Session,
127 128 RoutingKernelManager, NotebookManager,
128 129 KernelManager, SessionManager, RichIPythonWidget]
129 130 flags = Dict(flags)
130 131 aliases = Dict(aliases)
131 132
132 133 kernel_argv = List(Unicode)
133 134
134 135 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
135 136 default_value=logging.INFO,
136 137 config=True,
137 138 help="Set the log level by value or name.")
138 139
139 140 # connection info:
140 141 ip = Unicode(LOCALHOST, config=True,
141 142 help="The IP address the notebook server will listen on."
142 143 )
143 144
144 145 port = Int(8888, config=True,
145 146 help="The port the notebook server will listen on."
146 147 )
147 148
148 149 # the factory for creating a widget
149 150 widget_factory = Any(RichIPythonWidget)
150 151
151 152 def parse_command_line(self, argv=None):
152 153 super(IPythonNotebookApp, self).parse_command_line(argv)
153 154 if argv is None:
154 155 argv = sys.argv[1:]
155 156
156 157 self.kernel_argv = list(argv) # copy
157 158 # kernel should inherit default config file from frontend
158 159 self.kernel_argv.append("--KernelApp.parent_appname='%s'"%self.name)
159 160 # scrub frontend-specific flags
160 161 for a in argv:
161 162 if a.startswith('-') and a.lstrip('-') in notebook_flags:
162 163 self.kernel_argv.remove(a)
163 164
164 165 def init_configurables(self):
165 166 # Don't let Qt or ZMQ swallow KeyboardInterupts.
166 167 signal.signal(signal.SIGINT, signal.SIG_DFL)
167 168
168 169 # Create a KernelManager and start a kernel.
169 170 self.kernel_manager = KernelManager(config=self.config, log=self.log)
170 171 self.routing_kernel_manager = RoutingKernelManager(config=self.config, log=self.log,
171 172 kernel_manager=self.kernel_manager, kernel_argv=self.kernel_argv
172 173 )
173 174 self.notebook_manager = NotebookManager(config=self.config, log=self.log)
174 175
175 176 def init_logging(self):
176 177 super(IPythonNotebookApp, self).init_logging()
177 178 # This prevents double log messages because tornado use a root logger that
178 179 # self.log is a child of. The logging module dipatches log messages to a log
179 180 # and all of its ancenstors until propagate is set to False.
180 181 self.log.propagate = False
181 182
182 183 def initialize(self, argv=None):
183 184 super(IPythonNotebookApp, self).initialize(argv)
184 185 self.init_configurables()
185 186 self.web_app = NotebookWebApplication(
186 187 self.routing_kernel_manager, self.notebook_manager, self.log
187 188 )
188 189 self.http_server = httpserver.HTTPServer(self.web_app)
189 190 self.http_server.listen(self.port)
190 191
191 192 def start(self):
192 193 self.log.info("The IPython Notebook is running at: http://%s:%i" % (self.ip, self.port))
193 194 ioloop.IOLoop.instance().start()
194 195
195 196 #-----------------------------------------------------------------------------
196 197 # Main entry point
197 198 #-----------------------------------------------------------------------------
198 199
199 200 def launch_new_instance():
200 201 app = IPythonNotebookApp()
201 202 app.initialize()
202 203 app.start()
203 204
@@ -1,286 +1,303 b''
1 1
2 2 /**
3 3 * Primary styles
4 4 *
5 5 * Author: IPython Development Team
6 6 */
7 7
8 8
9 9 body {
10 10 background-color: white;
11 11 /* This makes sure that the body covers the entire window and needs to
12 12 be in a different element than the display: box in wrapper below */
13 13 position: absolute;
14 14 left: 0px;
15 15 right: 0px;
16 16 top: 0px;
17 17 bottom: 0px;
18 18 overflow: hidden;
19 19 }
20 20
21 21 span#save_widget {
22 22 position: absolute;
23 23 left: 0px;
24 24 padding: 5px 0px;
25 25 margin: 0px 0px 0px 0px;
26 26 }
27 27
28 28 input#notebook_name {
29 29 height: 1em;
30 30 line-height: 1em;
31 31 padding: 5px;
32 32 }
33 33
34 34 span#kernel_status {
35 35 position: absolute;
36 36 padding: 8px 5px 5px 5px;
37 37 right: 10px;
38 38 font-weight: bold;
39 39 }
40 40
41 41 .status_idle {
42 42 color: gray;
43 43 }
44 44
45 45 .status_busy {
46 46 color: red;
47 47 }
48 48
49 49 .status_restarting {
50 50 color: black;
51 51 }
52 52
53 53 div#left_panel {
54 54 overflow-y: auto;
55 55 top: 0px;
56 56 left: 0px;
57 57 margin: 0px;
58 58 padding: 0px;
59 59 position: absolute;
60 60 }
61 61
62 62 h3.section_header {
63 63 padding: 5px;
64 64 }
65 65
66 66 div.section_content {
67 67 padding: 5px;
68 68 }
69 69
70 70
71 71 span.section_row_buttons > button {
72 72 width: 60px;
73 73 }
74 74
75 75 .section_row {
76 76 margin: 5px 0px;
77 77 }
78 78
79 79 .section_row_buttons {
80 80 float: right;
81 81 }
82 82
83 83 #kernel_persist {
84 84 float: right;
85 85 }
86 86
87 87 .checkbox_label {
88 88 font-size: 85%;
89 89 float: right;
90 90 padding: 0.3em;
91 91 }
92 92
93 93 .section_row_header {
94 94 float: left;
95 95 font-size: 85%;
96 96 padding: 0.4em 0em;
97 97 font-weight: bold;
98 98 }
99 99
100 100 span.button_label {
101 101 padding: 0.2em 1em;
102 102 font-size: 77%;
103 103 float: right;
104 104 }
105 105
106 106 /* This is needed because FF was adding a 2px margin top and bottom. */
107 107 .section_row .ui-button {
108 108 margin-top: 0px;
109 109 margin-bottom: 0px;
110 110 }
111 111
112 112 #download_format {
113 113 float: right;
114 114 font-size: 85%;
115 115 width: 60px;
116 116 margin: 1px 5px;
117 117 }
118 118
119 119 div#left_panel_splitter {
120 120 width: 8px;
121 121 top: 0px;
122 122 left: 202px;
123 123 margin: 0px;
124 124 padding: 0px;
125 125 position: absolute;
126 126 }
127 127
128 128 div#notebook_panel {
129 129 /* The L margin will be set in the Javascript code*/
130 130 margin: 0px 0px 0px 0px;
131 131 padding: 0px;
132 132 }
133 133
134 134 div#notebook {
135 135 overflow-y: scroll;
136 136 overflow-x: auto;
137 137 width: 100%;
138 138 padding: 0px 15px 0px 15px;
139 139 margin: 0px
140 140 background-color: white;
141 141 }
142 142
143 143 div#pager_splitter {
144 144 height: 8px;
145 145 }
146 146
147 147 div#pager {
148 148 padding: 15px;
149 149 overflow: auto;
150 150 }
151 151
152 152 div.cell {
153 153 width: 100%;
154 154 padding: 5px;
155 155 /* This acts as a spacer between cells, that is outside the border */
156 156 margin: 15px 0px 15px 0px;
157 157 }
158 158
159 159 div.code_cell {
160 160 background-color: white;
161 161 }
162 162
163 163 div.prompt {
164 164 width: 80px;
165 165 padding: 0.4em;
166 166 margin: 0px;
167 167 font-family: monospace;
168 168 }
169 169
170 170 div.input_prompt {
171 171 color: navy;
172 172 }
173 173
174 174 div.output {
175 175 /* This is a spacer between the input and output of each cell */
176 176 margin-top: 15px;
177 177 }
178 178
179 179 div.output_prompt {
180 180 color: darkred;
181 181 }
182 182
183 183 div.input_area {
184 184 color: black;
185 185 }
186 186
187 187 div.output_area {
188 188 text-align: left;
189 189 color: black;
190 190 font-family: monospace;
191 191 }
192 192
193 193 div.output_stream {
194 194 padding: 0.4em;
195 195 }
196 196
197 197 div.output_latex {
198 198 /* Slightly bigger than the rest of the notebook */
199 199 font-size: 116%;
200 200 }
201 201
202 202 div.output_html {
203 203 }
204 204
205 205 div.output_png {
206 206 }
207 207
208 208 div.html_cell {
209 209 background-color: white;
210 210 }
211 211
212 212 div.html_cell_input {
213 213 color: black;
214 214 }
215 215
216 216 div.html_cell_render {
217 217 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
218 218 /* Slightly bigger than the rest of the notebook */
219 219 font-size: 116%;
220 220 outline: none;
221 221 resize: none;
222 222 width: inherit;
223 223 border-style: none;
224 224 padding: 5px;
225 225 color: black;
226 226 }
227 227
228 div.html_cell_render em {font-style: italic;}
229 div.html_cell_render strong {font-weight: bold;}
230 div.html_cell_render u {text-decoration: underline;}
231 div.html_cell_render :link { text-decoration: underline }
232 div.html_cell_render :visited { text-decoration: underline }
233 div.html_cell_render h1 {font-size: 197%; margin: .67em 0; font-weight: bold;}
234 div.html_cell_render h2 {font-size: 153.9%; margin: .75em 0; font-weight: bold;}
235 div.html_cell_render h3 {font-size: 116%; margin: .83em 0; font-weight: bold;}
236 div.html_cell_render h4 {margin: 1.12em 0; font-weight: bold;}
237 div.html_cell_render h5 {font-size: 85%.; margin: 1.5em 0; font-weight: bold;}
238 div.html_cell_render h6 {font-size: 77%; margin: 1.67em 0; font-weight: bold;}
239 div.html_cell_render ul {list-style:disc; margin-left: 40px;}
240 div.html_cell_render ul ul {list-style:square; margin-left: 40px;}
241 div.html_cell_render ul ul ul {list-style:circle; margin-left: 40px;}
242 div.html_cell_render ol {list-style:upper-roman; margin-left: 40px;}
243 div.html_cell_render ol ol {list-style:upper-alpha;}
244 div.html_cell_render ol ol ol {list-style:decimal;}
245 div.html_cell_render ol ol ol ol {list-style:lower-alpha;}
246 div.html_cell_render ol ol ol ol ol {list-style:lower-roman;}
228 div.rst_cell_input {
229 color: black;
230 }
231
232 div.rst_cell_render {
233 font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif;
234 /* Slightly bigger than the rest of the notebook */
235 font-size: 116%;
236 outline: none;
237 resize: none;
238 width: inherit;
239 border-style: none;
240 padding: 5px;
241 color: black;
242 }
243
244 .rendered_html {color: black;}
245 .rendered_html em {font-style: italic;}
246 .rendered_html strong {font-weight: bold;}
247 .rendered_html u {text-decoration: underline;}
248 .rendered_html :link { text-decoration: underline }
249 .rendered_html :visited { text-decoration: underline }
250 .rendered_html h1 {font-size: 197%; margin: .67em 0; font-weight: bold;}
251 .rendered_html h2 {font-size: 153.9%; margin: .75em 0; font-weight: bold;}
252 .rendered_html h3 {font-size: 116%; margin: .83em 0; font-weight: bold;}
253 .rendered_html h4 {margin: 1.12em 0; font-weight: bold;}
254 .rendered_html h5 {font-size: 85%.; margin: 1.5em 0; font-weight: bold;}
255 .rendered_html h6 {font-size: 77%; margin: 1.67em 0; font-weight: bold;}
256 .rendered_html ul {list-style:disc; margin-left: 40px;}
257 .rendered_html ul ul {list-style:square; margin-left: 40px;}
258 .rendered_html ul ul ul {list-style:circle; margin-left: 40px;}
259 .rendered_html ol {list-style:upper-roman; margin-left: 40px;}
260 .rendered_html ol ol {list-style:upper-alpha;}
261 .rendered_html ol ol ol {list-style:decimal;}
262 .rendered_html ol ol ol ol {list-style:lower-alpha;}
263 .rendered_html ol ol ol ol ol {list-style:lower-roman;}
247 264
248 265 .CodeMirror {
249 266 line-height: 1.231; /* Changed from 1em to our global default */
250 267 }
251 268
252 269 .CodeMirror-scroll {
253 270 height: auto; /* Changed to auto to autogrow */
254 271 overflow-y: visible; /* Changed from auto to remove scrollbar */
255 272 overflow-x: auto; /* Changed from auto to remove scrollbar */
256 273 }
257 274
258 275 /* CSS font colors for translated ANSI colors. */
259 276
260 277
261 278 .ansiblack {color: black;}
262 279 .ansired {color: darkred;}
263 280 .ansigreen {color: darkgreen;}
264 281 .ansiyellow {color: brown;}
265 282 .ansiblue {color: darkblue;}
266 283 .ansipurple {color: darkviolet;}
267 284 .ansicyan {color: steelblue;}
268 285 .ansigrey {color: grey;}
269 286 .ansibold {font-weight: bold;}
270 287
271 288 .completions {
272 289 position: absolute;
273 290 z-index: 10;
274 291 overflow: auto;
275 292 border: 1px solid black;
276 293 }
277 294
278 295 .completions select {
279 296 background: white;
280 297 outline: none;
281 298 border: none;
282 299 padding: 0px;
283 300 margin: 0px;
284 301 font-family: monospace;
285 302 }
286 303
@@ -1,155 +1,156 b''
1 1
2 2 //============================================================================
3 3 // HTMLCell
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var HTMLCell = function (notebook) {
9 9 IPython.Cell.apply(this, arguments);
10 10 this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"
11 11 this.rendered = false;
12 12 };
13 13
14 14
15 15 HTMLCell.prototype = new IPython.Cell();
16 16
17 17
18 18
19 19 HTMLCell.prototype.create_element = function () {
20 20 var cell = $("<div>").addClass('cell html_cell border-box-sizing');
21 21 var input_area = $('<div/>').addClass('html_cell_input');
22 22 this.code_mirror = CodeMirror(input_area.get(0), {
23 23 indentUnit : 4,
24 24 enterMode : 'flat',
25 25 tabMode: 'shift',
26 26 mode: 'htmlmixed',
27 27 theme: 'default',
28 28 value: this.placeholder
29 29 });
30 30 // The tabindex=-1 makes this div focusable.
31 var render_area = $('<div/>').addClass('html_cell_render').attr('tabindex','-1');
31 var render_area = $('<div/>').addClass('html_cell_render').
32 addClass('rendered_html').attr('tabindex','-1');
32 33 cell.append(input_area).append(render_area);
33 34 this.element = cell;
34 35 };
35 36
36 37
37 38 HTMLCell.prototype.bind_events = function () {
38 39 IPython.Cell.prototype.bind_events.apply(this);
39 40 var that = this;
40 41 this.element.keydown(function (event) {
41 42 if (event.which === 13) {
42 43 if (that.rendered) {
43 44 that.edit();
44 45 event.preventDefault();
45 46 };
46 47 };
47 48 });
48 49 };
49 50
50 51
51 52 HTMLCell.prototype.select = function () {
52 53 IPython.Cell.prototype.select.apply(this);
53 54 var output = this.element.find("div.html_cell_render");
54 55 output.trigger('focus');
55 56 };
56 57
57 58
58 59 HTMLCell.prototype.edit = function () {
59 60 if (this.rendered === true) {
60 61 var html_cell = this.element;
61 62 var output = html_cell.find("div.html_cell_render");
62 63 output.hide();
63 64 html_cell.find('div.html_cell_input').show();
64 65 this.code_mirror.focus();
65 66 this.code_mirror.refresh();
66 67 this.rendered = false;
67 68 };
68 69 };
69 70
70 71
71 72 HTMLCell.prototype.render = function () {
72 73 if (this.rendered === false) {
73 74 var html_cell = this.element;
74 75 var output = html_cell.find("div.html_cell_render");
75 76 var text = this.get_source();
76 77 if (text === "") {text = this.placeholder;};
77 this.set_render(text);
78 this.set_rendered(text);
78 79 MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
79 80 html_cell.find('div.html_cell_input').hide();
80 81 output.show();
81 82 this.rendered = true;
82 83 };
83 84 };
84 85
85 86
86 87 HTMLCell.prototype.config_mathjax = function () {
87 88 var html_cell = this.element;
88 89 var that = this;
89 90 html_cell.click(function () {
90 91 that.edit();
91 92 }).focusout(function () {
92 93 that.render();
93 94 });
94 95
95 96 html_cell.trigger("focusout");
96 97 };
97 98
98 99
99 100 HTMLCell.prototype.get_source = function() {
100 101 return this.code_mirror.getValue();
101 102 };
102 103
103 104
104 105 HTMLCell.prototype.set_source = function(text) {
105 106 this.code_mirror.setValue(text);
106 107 this.code_mirror.refresh();
107 108 };
108 109
109 110
110 HTMLCell.prototype.set_render = function(text) {
111 HTMLCell.prototype.set_rendered = function(text) {
111 112 this.element.find('div.html_cell_render').html(text);
112 113 };
113 114
114 115
115 116 HTMLCell.prototype.at_top = function () {
116 117 if (this.rendered) {
117 118 return true;
118 119 } else {
119 120 return false;
120 121 }
121 122 };
122 123
123 124
124 125 HTMLCell.prototype.at_bottom = function () {
125 126 if (this.rendered) {
126 127 return true;
127 128 } else {
128 129 return false;
129 130 }
130 131 };
131 132
132 133
133 134 HTMLCell.prototype.fromJSON = function (data) {
134 135 if (data.cell_type === 'html') {
135 136 if (data.source !== undefined) {
136 137 this.set_source(data.source);
137 this.set_render(data.source);
138 this.set_rendered(data.source);
138 139 };
139 140 };
140 141 }
141 142
142 143
143 144 HTMLCell.prototype.toJSON = function () {
144 145 var data = {}
145 146 data.cell_type = 'html';
146 147 data.source = this.get_source();
147 148 return data;
148 149 };
149 150
150 151 IPython.HTMLCell = HTMLCell;
151 152
152 153 return IPython;
153 154
154 155 }(IPython));
155 156
@@ -1,659 +1,711 b''
1 1
2 2 //============================================================================
3 3 // Notebook
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var Notebook = function (selector) {
11 11 this.element = $(selector);
12 12 this.element.scroll();
13 13 this.element.data("notebook", this);
14 14 this.next_prompt_number = 1;
15 15 this.kernel = null;
16 16 this.msg_cell_map = {};
17 17 this.style();
18 18 this.create_elements();
19 19 this.bind_events();
20 20 };
21 21
22 22
23 23 Notebook.prototype.style = function () {
24 24 $('div#notebook').addClass('border-box-sizing');
25 25 };
26 26
27 27
28 28 Notebook.prototype.create_elements = function () {
29 29 // We add this end_space div to the end of the notebook div to:
30 30 // i) provide a margin between the last cell and the end of the notebook
31 31 // ii) to prevent the div from scrolling up when the last cell is being
32 32 // edited, but is too low on the page, which browsers will do automatically.
33 33 this.element.append($('<div class="end_space"></div>').height(50));
34 34 $('div#notebook').addClass('border-box-sizing');
35 35 };
36 36
37 37
38 38 Notebook.prototype.bind_events = function () {
39 39 var that = this;
40 40 $(document).keydown(function (event) {
41 41 // console.log(event);
42 42 if (event.which === 38) {
43 43 var cell = that.selected_cell();
44 44 if (cell.at_top()) {
45 45 event.preventDefault();
46 46 that.select_prev();
47 47 };
48 48 } else if (event.which === 40) {
49 49 var cell = that.selected_cell();
50 50 if (cell.at_bottom()) {
51 51 event.preventDefault();
52 52 that.select_next();
53 53 };
54 54 } else if (event.which === 13 && event.shiftKey) {
55 55 that.execute_selected_cell();
56 56 return false;
57 57 } else if (event.which === 13 && event.ctrlKey) {
58 58 that.execute_selected_cell({terminal:true});
59 59 return false;
60 60 };
61 61 });
62 62
63 63 this.element.bind('collapse_pager', function () {
64 64 var app_height = $('div#main_app').height(); // content height
65 65 var splitter_height = $('div#pager_splitter').outerHeight(true);
66 66 var new_height = app_height - splitter_height;
67 67 that.element.animate({height : new_height + 'px'}, 'fast');
68 68 });
69 69
70 70 this.element.bind('expand_pager', function () {
71 71 var app_height = $('div#main_app').height(); // content height
72 72 var splitter_height = $('div#pager_splitter').outerHeight(true);
73 73 var pager_height = $('div#pager').outerHeight(true);
74 74 var new_height = app_height - pager_height - splitter_height;
75 75 that.element.animate({height : new_height + 'px'}, 'fast');
76 76 });
77 77
78 78 this.element.bind('collapse_left_panel', function () {
79 79 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
80 80 var new_margin = splitter_width;
81 81 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
82 82 });
83 83
84 84 this.element.bind('expand_left_panel', function () {
85 85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
86 86 var left_panel_width = IPython.left_panel.width;
87 87 var new_margin = splitter_width + left_panel_width;
88 88 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
89 89 });
90 90 };
91 91
92 92
93 93 Notebook.prototype.scroll_to_bottom = function () {
94 94 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
95 95 };
96 96
97 97
98 98 Notebook.prototype.scroll_to_top = function () {
99 99 this.element.animate({scrollTop:0}, 0);
100 100 };
101 101
102 102
103 103 // Cell indexing, retrieval, etc.
104 104
105 105
106 106 Notebook.prototype.cell_elements = function () {
107 107 return this.element.children("div.cell");
108 108 }
109 109
110 110
111 111 Notebook.prototype.ncells = function (cell) {
112 112 return this.cell_elements().length;
113 113 }
114 114
115 115
116 116 // TODO: we are often calling cells as cells()[i], which we should optimize
117 117 // to cells(i) or a new method.
118 118 Notebook.prototype.cells = function () {
119 119 return this.cell_elements().toArray().map(function (e) {
120 120 return $(e).data("cell");
121 121 });
122 122 }
123 123
124 124
125 125 Notebook.prototype.find_cell_index = function (cell) {
126 126 var result = null;
127 127 this.cell_elements().filter(function (index) {
128 128 if ($(this).data("cell") === cell) {
129 129 result = index;
130 130 };
131 131 });
132 132 return result;
133 133 };
134 134
135 135
136 136 Notebook.prototype.index_or_selected = function (index) {
137 137 return index || this.selected_index() || 0;
138 138 }
139 139
140 140
141 141 Notebook.prototype.select = function (index) {
142 142 if (index !== undefined && index >= 0 && index < this.ncells()) {
143 143 if (this.selected_index() !== null) {
144 144 this.selected_cell().unselect();
145 145 };
146 146 this.cells()[index].select();
147 147 if (index === (this.ncells()-1)) {
148 148 this.scroll_to_bottom();
149 149 };
150 150 };
151 151 return this;
152 152 };
153 153
154 154
155 155 Notebook.prototype.select_next = function () {
156 156 var index = this.selected_index();
157 157 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
158 158 this.select(index+1);
159 159 };
160 160 return this;
161 161 };
162 162
163 163
164 164 Notebook.prototype.select_prev = function () {
165 165 var index = this.selected_index();
166 166 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
167 167 this.select(index-1);
168 168 };
169 169 return this;
170 170 };
171 171
172 172
173 173 Notebook.prototype.selected_index = function () {
174 174 var result = null;
175 175 this.cell_elements().filter(function (index) {
176 176 if ($(this).data("cell").selected === true) {
177 177 result = index;
178 178 };
179 179 });
180 180 return result;
181 181 };
182 182
183 183
184 184 Notebook.prototype.cell_for_msg = function (msg_id) {
185 185 var cell_id = this.msg_cell_map[msg_id];
186 186 var result = null;
187 187 this.cell_elements().filter(function (index) {
188 188 cell = $(this).data("cell");
189 189 if (cell.cell_id === cell_id) {
190 190 result = cell;
191 191 };
192 192 });
193 193 return result;
194 194 };
195 195
196 196
197 197 Notebook.prototype.selected_cell = function () {
198 198 return this.cell_elements().eq(this.selected_index()).data("cell");
199 199 }
200 200
201 201
202 202 // Cell insertion, deletion and moving.
203 203
204 204
205 205 Notebook.prototype.delete_cell = function (index) {
206 206 var i = index || this.selected_index();
207 207 if (i !== null && i >= 0 && i < this.ncells()) {
208 208 this.cell_elements().eq(i).remove();
209 209 if (i === (this.ncells())) {
210 210 this.select(i-1);
211 211 } else {
212 212 this.select(i);
213 213 };
214 214 };
215 215 return this;
216 216 };
217 217
218 218
219 219 Notebook.prototype.append_cell = function (cell) {
220 220 this.element.find('div.end_space').before(cell.element);
221 221 return this;
222 222 };
223 223
224 224
225 225 Notebook.prototype.insert_cell_after = function (cell, index) {
226 226 var ncells = this.ncells();
227 227 if (ncells === 0) {
228 228 this.append_cell(cell);
229 229 return this;
230 230 };
231 231 if (index >= 0 && index < ncells) {
232 232 this.cell_elements().eq(index).after(cell.element);
233 233 };
234 234 return this
235 235 };
236 236
237 237
238 238 Notebook.prototype.insert_cell_before = function (cell, index) {
239 239 var ncells = this.ncells();
240 240 if (ncells === 0) {
241 241 this.append_cell(cell);
242 242 return this;
243 243 };
244 244 if (index >= 0 && index < ncells) {
245 245 this.cell_elements().eq(index).before(cell.element);
246 246 };
247 247 return this;
248 248 };
249 249
250 250
251 251 Notebook.prototype.move_cell_up = function (index) {
252 252 var i = index || this.selected_index();
253 253 if (i !== null && i < this.ncells() && i > 0) {
254 254 var pivot = this.cell_elements().eq(i-1);
255 255 var tomove = this.cell_elements().eq(i);
256 256 if (pivot !== null && tomove !== null) {
257 257 tomove.detach();
258 258 pivot.before(tomove);
259 259 this.select(i-1);
260 260 };
261 261 };
262 262 return this;
263 263 }
264 264
265 265
266 266 Notebook.prototype.move_cell_down = function (index) {
267 267 var i = index || this.selected_index();
268 268 if (i !== null && i < (this.ncells()-1) && i >= 0) {
269 269 var pivot = this.cell_elements().eq(i+1)
270 270 var tomove = this.cell_elements().eq(i)
271 271 if (pivot !== null && tomove !== null) {
272 272 tomove.detach();
273 273 pivot.after(tomove);
274 274 this.select(i+1);
275 275 };
276 276 };
277 277 return this;
278 278 }
279 279
280 280
281 281 Notebook.prototype.sort_cells = function () {
282 282 var ncells = this.ncells();
283 283 var sindex = this.selected_index();
284 284 var swapped;
285 285 do {
286 286 swapped = false
287 287 for (var i=1; i<ncells; i++) {
288 288 current = this.cell_elements().eq(i).data("cell");
289 289 previous = this.cell_elements().eq(i-1).data("cell");
290 290 if (previous.input_prompt_number > current.input_prompt_number) {
291 291 this.move_cell_up(i);
292 292 swapped = true;
293 293 };
294 294 };
295 295 } while (swapped);
296 296 this.select(sindex);
297 297 return this;
298 298 };
299 299
300 300
301 301 Notebook.prototype.insert_code_cell_before = function (index) {
302 302 // TODO: Bounds check for i
303 303 var i = this.index_or_selected(index);
304 304 var cell = new IPython.CodeCell(this);
305 305 cell.set_input_prompt();
306 306 this.insert_cell_before(cell, i);
307 307 this.select(this.find_cell_index(cell));
308 308 return cell;
309 309 }
310 310
311 311
312 312 Notebook.prototype.insert_code_cell_after = function (index) {
313 313 // TODO: Bounds check for i
314 314 var i = this.index_or_selected(index);
315 315 var cell = new IPython.CodeCell(this);
316 316 cell.set_input_prompt();
317 317 this.insert_cell_after(cell, i);
318 318 this.select(this.find_cell_index(cell));
319 319 return cell;
320 320 }
321 321
322 322
323 323 Notebook.prototype.insert_html_cell_before = function (index) {
324 324 // TODO: Bounds check for i
325 325 var i = this.index_or_selected(index);
326 326 var cell = new IPython.HTMLCell(this);
327 327 cell.config_mathjax();
328 328 this.insert_cell_before(cell, i);
329 329 this.select(this.find_cell_index(cell));
330 330 return cell;
331 331 }
332 332
333 333
334 334 Notebook.prototype.insert_html_cell_after = function (index) {
335 335 // TODO: Bounds check for i
336 336 var i = this.index_or_selected(index);
337 337 var cell = new IPython.HTMLCell(this);
338 338 cell.config_mathjax();
339 339 this.insert_cell_after(cell, i);
340 340 this.select(this.find_cell_index(cell));
341 341 return cell;
342 342 }
343 343
344 344
345 Notebook.prototype.html_to_code = function (index) {
345 Notebook.prototype.insert_rst_cell_before = function (index) {
346 // TODO: Bounds check for i
347 var i = this.index_or_selected(index);
348 var cell = new IPython.RSTCell(this);
349 cell.config_mathjax();
350 this.insert_cell_before(cell, i);
351 this.select(this.find_cell_index(cell));
352 return cell;
353 }
354
355
356 Notebook.prototype.insert_rst_cell_after = function (index) {
357 // TODO: Bounds check for i
358 var i = this.index_or_selected(index);
359 var cell = new IPython.RSTCell(this);
360 cell.config_mathjax();
361 this.insert_cell_after(cell, i);
362 this.select(this.find_cell_index(cell));
363 return cell;
364 }
365
366
367 Notebook.prototype.to_code = function (index) {
346 368 // TODO: Bounds check for i
347 369 var i = this.index_or_selected(index);
348 370 var source_element = this.cell_elements().eq(i);
349 371 var source_cell = source_element.data("cell");
350 if (source_cell instanceof IPython.HTMLCell) {
372 if (source_cell instanceof IPython.HTMLCell || source_cell instanceof IPython.RSTCell) {
351 373 this.insert_code_cell_after(i);
352 374 var target_cell = this.cells()[i+1];
353 375 target_cell.set_code(source_cell.get_source());
354 376 source_element.remove();
355 377 };
356 378 };
357 379
358 380
359 Notebook.prototype.code_to_html = function (index) {
381 Notebook.prototype.to_rst = function (index) {
360 382 // TODO: Bounds check for i
361 383 var i = this.index_or_selected(index);
362 384 var source_element = this.cell_elements().eq(i);
363 385 var source_cell = source_element.data("cell");
386 var target_cell = null;
364 387 if (source_cell instanceof IPython.CodeCell) {
365 this.insert_html_cell_after(i);
388 this.insert_rst_cell_after(i);
366 389 var target_cell = this.cells()[i+1];
367 390 var text = source_cell.get_code();
391 } else if (source_cell instanceof IPython.HTMLCell) {
392 this.insert_rst_cell_after(i);
393 var target_cell = this.cells()[i+1];
394 var text = source_cell.get_source();
395 }
396 if (target_cell !== null) {
368 397 if (text === "") {text = target_cell.placeholder;};
369 398 target_cell.set_source(text);
370 target_cell.set_render(text);
371 399 source_element.remove();
372 400 target_cell.edit();
401 }
373 402 };
403
404
405 Notebook.prototype.to_html = function (index) {
406 // TODO: Bounds check for i
407 var i = this.index_or_selected(index);
408 var source_element = this.cell_elements().eq(i);
409 var source_cell = source_element.data("cell");
410 var target_cell = null;
411 if (source_cell instanceof IPython.CodeCell) {
412 this.insert_html_cell_after(i);
413 var target_cell = this.cells()[i+1];
414 var text = source_cell.get_code();
415 } else if (source_cell instanceof IPython.RSTCell) {
416 this.insert_html_cell_after(i);
417 var target_cell = this.cells()[i+1];
418 var text = source_cell.get_source();
419 }
420 if (target_cell !== null) {
421 if (text === "") {text = target_cell.placeholder;};
422 target_cell.set_source(text);
423 source_element.remove();
424 target_cell.edit();
425 }
374 426 };
375 427
376 428
377 429 // Cell collapsing
378 430
379 431 Notebook.prototype.collapse = function (index) {
380 432 var i = this.index_or_selected(index);
381 433 this.cells()[i].collapse();
382 434 };
383 435
384 436
385 437 Notebook.prototype.expand = function (index) {
386 438 var i = this.index_or_selected(index);
387 439 this.cells()[i].expand();
388 440 };
389 441
390 442
391 443 // Kernel related things
392 444
393 445 Notebook.prototype.start_kernel = function () {
394 446 this.kernel = new IPython.Kernel();
395 447 var notebook_id = IPython.save_widget.get_notebook_id();
396 448 this.kernel.start_kernel(notebook_id, $.proxy(this.kernel_started, this));
397 449 };
398 450
399 451
400 452 Notebook.prototype.handle_shell_reply = function (e) {
401 453 reply = $.parseJSON(e.data);
402 454 var header = reply.header;
403 455 var content = reply.content;
404 456 var msg_type = header.msg_type;
405 457 // console.log(reply);
406 458 var cell = this.cell_for_msg(reply.parent_header.msg_id);
407 459 if (msg_type === "execute_reply") {
408 460 cell.set_input_prompt(content.execution_count);
409 461 } else if (msg_type === "complete_reply") {
410 462 cell.finish_completing(content.matched_text, content.matches);
411 463 };
412 464 var payload = content.payload || [];
413 465 this.handle_payload(payload);
414 466 };
415 467
416 468
417 469 Notebook.prototype.handle_payload = function (payload) {
418 470 var l = payload.length;
419 471 if (l > 0) {
420 472 IPython.pager.clear();
421 473 IPython.pager.expand();
422 474 };
423 475 for (var i=0; i<l; i++) {
424 476 IPython.pager.append_text(payload[i].text);
425 477 };
426 478 };
427 479
428 480
429 481 Notebook.prototype.handle_iopub_reply = function (e) {
430 482 reply = $.parseJSON(e.data);
431 483 var content = reply.content;
432 484 // console.log(reply);
433 485 var msg_type = reply.header.msg_type;
434 486 var cell = this.cell_for_msg(reply.parent_header.msg_id);
435 487 var output_types = ['stream','display_data','pyout','pyerr'];
436 488 if (output_types.indexOf(msg_type) >= 0) {
437 489 this.handle_output(cell, msg_type, content);
438 490 } else if (msg_type === "status") {
439 491 if (content.execution_state === "busy") {
440 492 IPython.kernel_status_widget.status_busy();
441 493 } else if (content.execution_state === "idle") {
442 494 IPython.kernel_status_widget.status_idle();
443 495 };
444 496 }
445 497 };
446 498
447 499
448 500 Notebook.prototype.handle_output = function (cell, msg_type, content) {
449 501 var json = {};
450 502 json.output_type = msg_type;
451 503 if (msg_type === "stream") {
452 504 json.text = content.data + '\n';
453 505 } else if (msg_type === "display_data") {
454 506 json = this.convert_mime_types(json, content.data);
455 507 } else if (msg_type === "pyout") {
456 508 json.prompt_number = content.execution_count;
457 509 json = this.convert_mime_types(json, content.data);
458 510 } else if (msg_type === "pyerr") {
459 511 json.ename = content.ename;
460 512 json.evalue = content.evalue;
461 513 json.traceback = content.traceback;
462 514 };
463 515 cell.append_output(json);
464 516 };
465 517
466 518
467 519 Notebook.prototype.convert_mime_types = function (json, data) {
468 520 if (data['text/plain'] !== undefined) {
469 521 json.text = data['text/plain'];
470 522 };
471 523 if (data['text/html'] !== undefined) {
472 524 json.html = data['text/html'];
473 525 };
474 526 if (data['image/svg+xml'] !== undefined) {
475 527 json.svg = data['image/svg+xml'];
476 528 };
477 529 if (data['image/png'] !== undefined) {
478 530 json.png = data['image/png'];
479 531 };
480 532 if (data['text/latex'] !== undefined) {
481 533 json.latex = data['text/latex'];
482 534 };
483 535 if (data['application/json'] !== undefined) {
484 536 json.json = data['application/json'];
485 537 };
486 538 if (data['application/javascript'] !== undefined) {
487 539 json.javascript = data['application/javascript'];
488 540 }
489 541 return json;
490 542 };
491 543
492 544 Notebook.prototype.kernel_started = function () {
493 545 console.log("Kernel started: ", this.kernel.kernel_id);
494 546 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
495 547 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
496 548 };
497 549
498 550
499 551 Notebook.prototype.execute_selected_cell = function (options) {
500 552 // add_new: should a new cell be added if we are at the end of the nb
501 553 // terminal: execute in terminal mode, which stays in the current cell
502 554 default_options = {terminal: false, add_new: true}
503 555 $.extend(default_options, options)
504 556 var that = this;
505 557 var cell = that.selected_cell();
506 558 var cell_index = that.find_cell_index(cell);
507 559 if (cell instanceof IPython.CodeCell) {
508 560 cell.clear_output();
509 561 var code = cell.get_code();
510 562 var msg_id = that.kernel.execute(cell.get_code());
511 563 that.msg_cell_map[msg_id] = cell.cell_id;
512 564 } else if (cell instanceof IPython.HTMLCell) {
513 565 cell.render();
514 566 }
515 567 if (default_options.terminal) {
516 568 cell.clear_input();
517 569 } else {
518 570 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
519 571 that.insert_code_cell_after();
520 572 // If we are adding a new cell at the end, scroll down to show it.
521 573 that.scroll_to_bottom();
522 574 } else {
523 575 that.select(cell_index+1);
524 576 };
525 577 };
526 578 };
527 579
528 580
529 581 Notebook.prototype.execute_all_cells = function () {
530 582 var ncells = this.ncells();
531 583 for (var i=0; i<ncells; i++) {
532 584 this.select(i);
533 585 this.execute_selected_cell({add_new:false});
534 586 };
535 587 this.scroll_to_bottom();
536 588 };
537 589
538 590
539 591 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
540 592 var msg_id = this.kernel.complete(line, cursor_pos);
541 593 this.msg_cell_map[msg_id] = cell.cell_id;
542 594 };
543 595
544 596 // Persistance and loading
545 597
546 598
547 599 Notebook.prototype.fromJSON = function (data) {
548 600 var ncells = this.ncells();
549 601 for (var i=0; i<ncells; i++) {
550 602 // Always delete cell 0 as they get renumbered as they are deleted.
551 603 this.delete_cell(0);
552 604 };
553 605 // Only handle 1 worksheet for now.
554 606 var worksheet = data.worksheets[0];
555 607 if (worksheet !== undefined) {
556 608 var new_cells = worksheet.cells;
557 609 ncells = new_cells.length;
558 610 var cell_data = null;
559 611 var new_cell = null;
560 612 for (var i=0; i<ncells; i++) {
561 613 cell_data = new_cells[i];
562 614 if (cell_data.cell_type == 'code') {
563 615 new_cell = this.insert_code_cell_after();
564 616 new_cell.fromJSON(cell_data);
565 617 } else if (cell_data.cell_type === 'html') {
566 618 new_cell = this.insert_html_cell_after();
567 619 new_cell.fromJSON(cell_data);
568 620 };
569 621 };
570 622 };
571 623 };
572 624
573 625
574 626 Notebook.prototype.toJSON = function () {
575 627 var cells = this.cells();
576 628 var ncells = cells.length;
577 629 cell_array = new Array(ncells);
578 630 for (var i=0; i<ncells; i++) {
579 631 cell_array[i] = cells[i].toJSON();
580 632 };
581 633 data = {
582 634 // Only handle 1 worksheet for now.
583 635 worksheets : [{cells:cell_array}]
584 636 }
585 637 return data
586 638 };
587 639
588 640 Notebook.prototype.save_notebook = function () {
589 641 if (IPython.save_widget.test_notebook_name()) {
590 642 var notebook_id = IPython.save_widget.get_notebook_id();
591 643 var nbname = IPython.save_widget.get_notebook_name();
592 644 // We may want to move the name/id/nbformat logic inside toJSON?
593 645 var data = this.toJSON();
594 646 data.name = nbname;
595 647 data.nbformat = 2;
596 648 data.id = notebook_id
597 649 // We do the call with settings so we can set cache to false.
598 650 var settings = {
599 651 processData : false,
600 652 cache : false,
601 653 type : "PUT",
602 654 data : JSON.stringify(data),
603 655 headers : {'Content-Type': 'application/json'},
604 656 success : $.proxy(this.notebook_saved,this)
605 657 };
606 658 IPython.save_widget.status_saving();
607 659 $.ajax("/notebooks/" + notebook_id, settings);
608 660 };
609 661 };
610 662
611 663
612 664 Notebook.prototype.notebook_saved = function (data, status, xhr) {
613 665 IPython.save_widget.status_save();
614 666 }
615 667
616 668
617 669 Notebook.prototype.load_notebook = function (callback) {
618 670 var that = this;
619 671 var notebook_id = IPython.save_widget.get_notebook_id();
620 672 // We do the call with settings so we can set cache to false.
621 673 var settings = {
622 674 processData : false,
623 675 cache : false,
624 676 type : "GET",
625 677 dataType : "json",
626 678 success : function (data, status, xhr) {
627 679 that.notebook_loaded(data, status, xhr);
628 680 if (callback !== undefined) {
629 681 callback();
630 682 };
631 683 }
632 684 };
633 685 IPython.save_widget.status_loading();
634 686 $.ajax("/notebooks/" + notebook_id, settings);
635 687 }
636 688
637 689
638 690 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
639 691 this.fromJSON(data);
640 692 if (this.ncells() === 0) {
641 693 this.insert_code_cell_after();
642 694 };
643 695 IPython.save_widget.status_save();
644 696 IPython.save_widget.set_notebook_name(data.name);
645 697 this.start_kernel();
646 698 // fromJSON always selects the last cell inserted. We need to wait
647 699 // until that is done before scrolling to the top.
648 700 setTimeout(function () {
649 701 IPython.notebook.select(0);
650 702 IPython.notebook.scroll_to_top();
651 703 }, 50);
652 704 };
653 705
654 706 IPython.Notebook = Notebook;
655 707
656 708 return IPython;
657 709
658 710 }(IPython));
659 711
@@ -1,50 +1,51 b''
1 1
2 2 //============================================================================
3 3 // On document ready
4 4 //============================================================================
5 5
6 6
7 7 $(document).ready(function () {
8 8
9 9 MathJax.Hub.Config({
10 10 tex2jax: {
11 11 inlineMath: [ ['$','$'], ["\\(","\\)"] ],
12 12 displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
13 13 },
14 14 displayAlign: 'left', // Change this to 'center' to center equations.
15 15 "HTML-CSS": {
16 16 styles: {'.MathJax_Display': {"margin": 0}}
17 17 }
18 18 });
19 IPython.markdown_converter = new Markdown.Converter();
19 20
20 21 $('div#header').addClass('border-box-sizing');
21 22 $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content');
22 23 $('div#notebook_panel').addClass('border-box-sizing ui-widget');
23 24
24 25 IPython.layout_manager = new IPython.LayoutManager();
25 26 IPython.pager = new IPython.Pager('div#pager', 'div#pager_splitter');
26 27 IPython.left_panel = new IPython.LeftPanel('div#left_panel', 'div#left_panel_splitter');
27 28 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
28 29 IPython.notebook = new IPython.Notebook('div#notebook');
29 30 IPython.kernel_status_widget = new IPython.KernelStatusWidget('#kernel_status');
30 31 IPython.kernel_status_widget.status_idle();
31 32
32 33 IPython.layout_manager.do_resize();
33 34
34 35 // These have display: none in the css file and are made visible here to prevent FLOUC.
35 36 $('div#header').css('display','block');
36 37 $('div#main_app').css('display','block');
37 38
38 39 // Perform these actions after the notebook has been loaded.
39 40 // We wait 100 milliseconds because the notebook scrolls to the top after a load
40 41 // is completed and we need to wait for that to mostly finish.
41 42 IPython.notebook.load_notebook(function () {
42 43 setTimeout(function () {
43 44 IPython.save_widget.update_url();
44 45 IPython.layout_manager.do_resize();
45 46 IPython.pager.collapse();
46 47 },100);
47 48 });
48 49
49 50 });
50 51
@@ -1,235 +1,238 b''
1 1
2 2 //============================================================================
3 3 // Cell
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 // Base PanelSection class
11 11
12 12 var PanelSection = function (selector) {
13 13 this.selector = selector;
14 14 if (this.selector !== undefined) {
15 15 this.element = $(selector);
16 16 this.header = this.element.find('h3.section_header');
17 17 this.content = this.element.find('div.section_content');
18 18 this.style();
19 19 this.bind_events();
20 20 }
21 21 this.expanded = true;
22 22 };
23 23
24 24
25 25 PanelSection.prototype.style = function () {
26 26 this.header.addClass('ui-widget ui-state-default');
27 27 this.content.addClass('ui-widget section_content');
28 28 };
29 29
30 30
31 31 PanelSection.prototype.bind_events = function () {
32 32 var that = this;
33 33 this.header.click(function () {
34 34 that.toggle();
35 35 });
36 36 this.header.hover(function () {
37 37 that.header.toggleClass('ui-state-hover');
38 38 });
39 39 };
40 40
41 41
42 42 PanelSection.prototype.expand = function () {
43 43 if (!this.expanded) {
44 44 this.content.slideDown('fast');
45 45 this.expanded = true;
46 46 };
47 47 };
48 48
49 49
50 50 PanelSection.prototype.collapse = function () {
51 51 if (this.expanded) {
52 52 this.content.slideUp('fast');
53 53 this.expanded = false;
54 54 };
55 55 };
56 56
57 57
58 58 PanelSection.prototype.toggle = function () {
59 59 if (this.expanded === true) {
60 60 this.collapse();
61 61 } else {
62 62 this.expand();
63 63 };
64 64 };
65 65
66 66
67 67 PanelSection.prototype.create_children = function () {};
68 68
69 69
70 70 // NotebookSection
71 71
72 72 var NotebookSection = function () {
73 73 PanelSection.apply(this, arguments);
74 74 };
75 75
76 76
77 77 NotebookSection.prototype = new PanelSection();
78 78
79 79
80 80 NotebookSection.prototype.style = function () {
81 81 PanelSection.prototype.style.apply(this);
82 82 this.content.addClass('ui-helper-clearfix');
83 83 this.content.find('div.section_row').addClass('ui-helper-clearfix');
84 84 this.content.find('#new_open').buttonset();
85 85 this.content.find('#download_notebook').button();
86 86 this.content.find('#upload_notebook').button();
87 87 this.content.find('#download_format').addClass('ui-widget ui-widget-content');
88 88 this.content.find('#download_format option').addClass('ui-widget ui-widget-content');
89 89 };
90 90
91 91
92 92 NotebookSection.prototype.bind_events = function () {
93 93 PanelSection.prototype.bind_events.apply(this);
94 94 var that = this;
95 95 this.content.find('#new_notebook').click(function () {
96 96 window.open('/new');
97 97 });
98 98 this.content.find('#open_notebook').click(function () {
99 99 window.open('/');
100 100 });
101 101 this.content.find('#download_notebook').click(function () {
102 102 var format = that.content.find('#download_format').val();
103 103 var notebook_id = IPython.save_widget.get_notebook_id();
104 104 var url = '/notebooks/' + notebook_id + '?format=' + format;
105 105 window.open(url,'_newtab');
106 106 });
107 107 };
108 108
109 109 // CellSection
110 110
111 111 var CellSection = function () {
112 112 PanelSection.apply(this, arguments);
113 113 };
114 114
115 115
116 116 CellSection.prototype = new PanelSection();
117 117
118 118
119 119 CellSection.prototype.style = function () {
120 120 PanelSection.prototype.style.apply(this);
121 121 this.content.addClass('ui-helper-clearfix');
122 122 this.content.find('div.section_row').addClass('ui-helper-clearfix');
123 123 this.content.find('#delete_cell').button();
124 124 this.content.find('#insert').buttonset();
125 125 this.content.find('#move').buttonset();
126 126 this.content.find('#cell_type').buttonset();
127 127 this.content.find('#toggle_output').buttonset();
128 128 this.content.find('#run_cells').buttonset();
129 129 };
130 130
131 131
132 132 CellSection.prototype.bind_events = function () {
133 133 PanelSection.prototype.bind_events.apply(this);
134 134 this.content.find('#collapse_cell').click(function () {
135 135 IPython.notebook.collapse();
136 136 });
137 137 this.content.find('#expand_cell').click(function () {
138 138 IPython.notebook.expand();
139 139 });
140 140 this.content.find('#delete_cell').click(function () {
141 141 IPython.notebook.delete_cell();
142 142 });
143 143 this.content.find('#insert_cell_above').click(function () {
144 144 IPython.notebook.insert_code_cell_before();
145 145 });
146 146 this.content.find('#insert_cell_below').click(function () {
147 147 IPython.notebook.insert_code_cell_after();
148 148 });
149 149 this.content.find('#move_cell_up').click(function () {
150 150 IPython.notebook.move_cell_up();
151 151 });
152 152 this.content.find('#move_cell_down').click(function () {
153 153 IPython.notebook.move_cell_down();
154 154 });
155 155 this.content.find('#to_code').click(function () {
156 IPython.notebook.html_to_code();
156 IPython.notebook.to_code();
157 157 });
158 158 this.content.find('#to_html').click(function () {
159 IPython.notebook.code_to_html();
159 IPython.notebook.to_html();
160 });
161 this.content.find('#to_rst').click(function () {
162 IPython.notebook.to_rst();
160 163 });
161 164 this.content.find('#run_selected_cell').click(function () {
162 165 IPython.notebook.execute_selected_cell();
163 166 });
164 167 this.content.find('#run_all_cells').click(function () {
165 168 IPython.notebook.execute_all_cells();
166 169 });
167 170 };
168 171
169 172
170 173 // KernelSection
171 174
172 175 var KernelSection = function () {
173 176 PanelSection.apply(this, arguments);
174 177 };
175 178
176 179
177 180 KernelSection.prototype = new PanelSection();
178 181
179 182
180 183 KernelSection.prototype.style = function () {
181 184 PanelSection.prototype.style.apply(this);
182 185 this.content.addClass('ui-helper-clearfix');
183 186 this.content.find('div.section_row').addClass('ui-helper-clearfix');
184 187 this.content.find('#int_restart').buttonset();
185 188 };
186 189
187 190
188 191 KernelSection.prototype.bind_events = function () {
189 192 PanelSection.prototype.bind_events.apply(this);
190 193 this.content.find('#restart_kernel').click(function () {
191 194 IPython.notebook.kernel.restart();
192 195 });
193 196 this.content.find('#int_kernel').click(function () {
194 197 IPython.notebook.kernel.interrupt();
195 198 });
196 199 };
197 200
198 201
199 202 // HelpSection
200 203
201 204 var HelpSection = function () {
202 205 PanelSection.apply(this, arguments);
203 206 };
204 207
205 208
206 209 HelpSection.prototype = new PanelSection();
207 210
208 211
209 212 HelpSection.prototype.style = function () {
210 213 PanelSection.prototype.style.apply(this);
211 214 PanelSection.prototype.style.apply(this);
212 215 this.content.addClass('ui-helper-clearfix');
213 216 this.content.find('div.section_row').addClass('ui-helper-clearfix');
214 217 this.content.find('#help_buttons0').buttonset();
215 218 this.content.find('#help_buttons1').buttonset();
216 219 };
217 220
218 221
219 222 HelpSection.prototype.bind_events = function () {
220 223 PanelSection.prototype.bind_events.apply(this);
221 224 };
222 225
223 226
224 227 // Set module variables
225 228
226 229 IPython.PanelSection = PanelSection;
227 230 IPython.NotebookSection = NotebookSection;
228 231 IPython.CellSection = CellSection;
229 232 IPython.KernelSection = KernelSection;
230 233 IPython.HelpSection = HelpSection;
231 234
232 235 return IPython;
233 236
234 237 }(IPython));
235 238
@@ -1,201 +1,204 b''
1 1 <!DOCTYPE HTML>
2 2 <html>
3 3
4 4 <head>
5 5 <meta charset="utf-8">
6 6
7 7 <title>IPython Notebook</title>
8 8
9 9 <link rel="stylesheet" href="static/jquery/css/themes/aristo/jquery-wijmo.css" type="text/css" />
10 10 <!-- <link rel="stylesheet" href="static/jquery/css/themes/rocket/jquery-wijmo.css" type="text/css" /> -->
11 11 <!-- <link rel="stylesheet" href="static/jquery/css/themes/smoothness/jquery-ui-1.8.14.custom.css" type="text/css" />-->
12 12
13 13 <script type="text/javascript" src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML" charset="utf-8"></script>
14 14 <!-- <script type='text/javascript' src='static/mathjax/MathJax.js?config=TeX-AMS_HTML' charset='utf-8'></script> -->
15 15 <script type="text/javascript">
16 16 if (typeof MathJax == 'undefined') {
17 17 console.log("Trying to load local copy of MathJax");
18 18 document.write(unescape("%3Cscript type='text/javascript' src='static/mathjax/MathJax.js%3Fconfig=TeX-AMS_HTML' charset='utf-8'%3E%3C/script%3E"));
19 19 }
20 20 </script>
21 21
22 22 <link rel="stylesheet" href="static/codemirror-2.12/lib/codemirror.css">
23 23 <link rel="stylesheet" href="static/codemirror-2.12/mode/rst/rst.css">
24 24 <link rel="stylesheet" href="static/codemirror-2.12/theme/ipython.css">
25 25 <link rel="stylesheet" href="static/codemirror-2.12/theme/default.css">
26 26
27 27 <link rel="stylesheet" href="static/css/boilerplate.css" type="text/css" />
28 28 <link rel="stylesheet" href="static/css/layout.css" type="text/css" />
29 29 <link rel="stylesheet" href="static/css/base.css" type="text/css" />
30 30 <link rel="stylesheet" href="static/css/notebook.css" type="text/css" />
31 31
32 32 </head>
33 33
34 34 <body>
35 35
36 36 <div id="header">
37 37 <span id="ipython_notebook"><h1>IPython Notebook</h1></span>
38 38 <span id="save_widget">
39 39 <input type="text" id="notebook_name" size="20"></textarea>
40 40 <span id="notebook_id" style="display:none">{{notebook_id}}</span>
41 41 <button id="save_notebook">Save</button>
42 42 </span>
43 43 <span id="kernel_status">Idle</span>
44 44 </div>
45 45
46 46 <div id="main_app">
47 47
48 48 <div id="left_panel">
49 49
50 50 <div id="notebook_section">
51 51 <h3 class="section_header">Notebook</h3>
52 52 <div class="section_content">
53 53 <div class="section_row">
54 54 <span id="new_open" class="section_row_buttons">
55 55 <button id="new_notebook">New</button>
56 56 <button id="open_notebook">Open</button>
57 57 </span>
58 58 <span class="section_row_header">Actions</span>
59 59 </div>
60 60 <div class="section_row">
61 61 <span class="section_row_buttons">
62 62 <button id="download_notebook">Export</button>
63 63 </span>
64 64 <span>
65 65 <select id="download_format">
66 66 <option value="xml">xml</option>
67 67 <option value="json">json</option>
68 68 <option value="py">py</option>
69 69 </select>
70 70 </span>
71 71 </div>
72 72 </div>
73 73 </div>
74 74
75 75 <div id="cell_section">
76 76 <h3 class="section_header">Cell</h3>
77 77 <div class="section_content">
78 78 <div class="section_row">
79 79 <span class="section_row_buttons">
80 80 <button id="delete_cell">Delete</button>
81 81 </span>
82 82 <span class="section_row_header">Actions</span>
83 83 </div>
84 84 <div class="section_row">
85 85 <span id="insert" class="section_row_buttons">
86 86 <button id="insert_cell_above">Above</button>
87 87 <button id="insert_cell_below">Below</button>
88 88 </span>
89 89 <span class="button_label">Insert</span>
90 90 </div>
91 91 <div class="section_row">
92 92 <span id="move" class="section_row_buttons">
93 93 <button id="move_cell_up">Up</button>
94 94 <button id="move_cell_down">Down</button>
95 95 </span>
96 96 <span class="button_label">Move</span>
97 97 </div>
98 98 <div class="section_row">
99 99 <span id="cell_type" class="section_row_buttons">
100 100 <button id="to_code">Code</button>
101 101 <button id="to_html">HTML</button>
102 <button id="to_rst">RST</button>
102 103 </span>
103 <span class="button_label">Cell Type</span>
104 <span class="button_label">Format</span>
104 105 </div>
105 106 <div class="section_row">
106 107 <span id="toggle_output" class="section_row_buttons">
107 108 <button id="collapse_cell">Collapse</button>
108 109 <button id="expand_cell">Expand</button>
109 110 </span>
110 111 <span class="button_label">Output</span>
111 112 </div>
112 113 <div class="section_row">
113 114 <span id="run_cells" class="section_row_buttons">
114 115 <button id="run_selected_cell">Selected</button>
115 116 <button id="run_all_cells">All</button>
116 117 </span>
117 118 <span class="button_label">Run</span>
118 119 </div>
119 120 </div>
120 121 </div>
121 122
122 123 <div id="kernel_section">
123 124 <h3 class="section_header">Kernel</h3>
124 125 <div class="section_content">
125 126 <div class="section_row">
126 127 <span id="int_restart" class="section_row_buttons">
127 128 <button id="int_kernel">Interrupt</button>
128 129 <button id="restart_kernel">Restart</button>
129 130 </span>
130 131 <span class="section_row_header">Actions</span>
131 132 </div>
132 133 <div class="section_row">
133 134 <span id="kernel_persist">
134 135 <input type="checkbox" id="kill_kernel"></input>
135 136 </span>
136 137 <span class="checkbox_label">Kill kernel upon exit:</span>
137 138 </div>
138 139 </div>
139 140 </div>
140 141
141 142 <div id="help_section">
142 143 <h3 class="section_header">Help</h3>
143 144 <div class="section_content">
144 145 <div class="section_row">
145 146 <span id="help_buttons0" class="section_row_buttons">
146 147 <button id="python_help"><a href="http://docs.python.org" target="_blank">Python</a></button>
147 148 <button id="ipython_help"><a href="http://ipython.org/documentation.html" target="_blank">IPython</a></button>
148 149 <button id="numpy_help"><a href="http://docs.scipy.org/doc/numpy/reference/" target="_blank">NumPy</a></button>
149 150 </span>
150 151 <span class="section_row_header">Links</span>
151 152 </div>
152 153 <div class="section_row">
153 154 <span id="help_buttons1" class="section_row_buttons">
154 155 <button id="matplotlib_help"><a href="http://matplotlib.sourceforge.net/" target="_blank">MPL</a></button>
155 156 <button id="scipy_help"><a href="http://docs.scipy.org/doc/scipy/reference/" target="_blank">SciPy</a></button>
156 157 <button id="sympy_help"><a href="http://docs.sympy.org/dev/index.html" target="_blank">SymPy</a></button>
157 158 </span>
158 159 </div>
159 160 </div>
160 161 </div>
161 162
162 163 </div>
163 164 <div id="left_panel_splitter"></div>
164 165 <div id="notebook_panel">
165 166 <div id="notebook"></div>
166 167 <div id="pager_splitter"></div>
167 168 <div id="pager"></div>
168 169 </div>
169 170
170 171 </div>
171 172
172 173 <script src="static/jquery/js/jquery-1.6.2.min.js" type="text/javascript" charset="utf-8"></script>
173 174 <script src="static/jquery/js/jquery-ui-1.8.14.custom.min.js" type="text/javascript" charset="utf-8"></script>
174 175 <script src="static/jquery/js/jquery.autogrow.js" type="text/javascript" charset="utf-8"></script>
175 176 <script src="static/js/namespace.js" type="text/javascript" charset="utf-8"></script>
176 177 <script src="static/js/utils.js" type="text/javascript" charset="utf-8"></script>
177 178 <script src="static/js/cell.js" type="text/javascript" charset="utf-8"></script>
178 179 <script src="static/js/codecell.js" type="text/javascript" charset="utf-8"></script>
179 180 <script src="static/js/htmlcell.js" type="text/javascript" charset="utf-8"></script>
181 <script src="static/js/rstcell.js" type="text/javascript" charset="utf-8"></script>
180 182 <script src="static/js/kernel.js" type="text/javascript" charset="utf-8"></script>
181 183 <script src="static/js/kernelstatus.js" type="text/javascript" charset="utf-8"></script>
182 184 <script src="static/js/layout.js" type="text/javascript" charset="utf-8"></script>
183 185 <script src="static/js/savewidget.js" type="text/javascript" charset="utf-8"></script>
184 186 <script src="static/js/pager.js" type="text/javascript" charset="utf-8"></script>
185 187 <script src="static/js/panelsection.js" type="text/javascript" charset="utf-8"></script>
186 188 <script src="static/js/leftpanel.js" type="text/javascript" charset="utf-8"></script>
187 189 <script src="static/js/notebook.js" type="text/javascript" charset="utf-8"></script>
188 190 <script src="static/js/notebook_main.js" type="text/javascript" charset="utf-8"></script>
189 <script src="static/codemirror-2.12/lib/codemirror.js"></script>
190 <script src="static/codemirror-2.12/mode/python/python.js"></script>
191 <script src="static/codemirror-2.12/mode/htmlmixed/htmlmixed.js"></script>
192 <script src="static/codemirror-2.12/mode/xml/xml.js"></script>
193 <script src="static/codemirror-2.12/mode/javascript/javascript.js"></script>
194 <script src="static/codemirror-2.12/mode/css/css.js"></script>
195 <script src="static/codemirror-2.12/mode/rst/rst.js"></script>
191 <script src="static/codemirror-2.12/lib/codemirror.js" charset="utf-8"></script>
192 <script src="static/codemirror-2.12/mode/python/python.js" charset="utf-8"></script>
193 <script src="static/codemirror-2.12/mode/htmlmixed/htmlmixed.js" charset="utf-8"></script>
194 <script src="static/codemirror-2.12/mode/xml/xml.js" charset="utf-8"></script>
195 <script src="static/codemirror-2.12/mode/javascript/javascript.js" charset="utf-8"></script>
196 <script src="static/codemirror-2.12/mode/css/css.js" charset="utf-8"></script>
197 <script src="static/codemirror-2.12/mode/rst/rst.js" charset="utf-8"></script>
198 <script src="static/pagedown/Markdown.Converter.js" charset="utf-8"></script>
196 199
197 200 </body>
198 201
199 202 </html>
200 203
201 204
General Comments 0
You need to be logged in to leave comments. Login now