##// 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 })();
@@ -6,11 +6,17 b''
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
@@ -156,3 +162,51 b' class NotebookHandler(web.RequestHandler):'
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
@@ -32,7 +32,7 b' 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
@@ -75,7 +75,8 b' class NotebookWebApplication(web.Application):'
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"),
@@ -225,25 +225,42 b' div.html_cell_render {'
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 */
@@ -28,7 +28,8 b' var IPython = (function (IPython) {'
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 };
@@ -74,7 +75,7 b' var IPython = (function (IPython) {'
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();
@@ -107,7 +108,7 b' var IPython = (function (IPython) {'
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
@@ -134,7 +135,7 b' var IPython = (function (IPython) {'
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 }
@@ -342,12 +342,34 b' var IPython = (function (IPython) {'
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());
@@ -356,21 +378,51 b' var IPython = (function (IPython) {'
356 378 };
357 379
358 380
359 Notebook.prototype.code_to_html = function (index) {
381 Notebook.prototype.to_rst = function (index) {
382 // TODO: Bounds check for i
383 var i = this.index_or_selected(index);
384 var source_element = this.cell_elements().eq(i);
385 var source_cell = source_element.data("cell");
386 var target_cell = null;
387 if (source_cell instanceof IPython.CodeCell) {
388 this.insert_rst_cell_after(i);
389 var target_cell = this.cells()[i+1];
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) {
397 if (text === "") {text = target_cell.placeholder;};
398 target_cell.set_source(text);
399 source_element.remove();
400 target_cell.edit();
401 }
402 };
403
404
405 Notebook.prototype.to_html = function (index) {
360 406 // TODO: Bounds check for i
361 407 var i = this.index_or_selected(index);
362 408 var source_element = this.cell_elements().eq(i);
363 409 var source_cell = source_element.data("cell");
410 var target_cell = null;
364 411 if (source_cell instanceof IPython.CodeCell) {
365 412 this.insert_html_cell_after(i);
366 413 var target_cell = this.cells()[i+1];
367 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) {
368 421 if (text === "") {text = target_cell.placeholder;};
369 422 target_cell.set_source(text);
370 target_cell.set_render(text);
371 423 source_element.remove();
372 424 target_cell.edit();
373 };
425 }
374 426 };
375 427
376 428
@@ -16,6 +16,7 b' $(document).ready(function () {'
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');
@@ -153,10 +153,13 b' var IPython = (function (IPython) {'
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();
@@ -99,8 +99,9 b''
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">
@@ -177,6 +178,7 b''
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>
@@ -186,13 +188,14 b''
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
General Comments 0
You need to be logged in to leave comments. Login now