s around 300 | // "paragraphs" that are wrapped in non-block-level tags, such as anchors, 301 | // phrase emphasis, and spans. The list of tags we're looking for is 302 | // hard-coded: 303 | var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" 304 | var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" 305 | 306 | // First, look for nested blocks, e.g.: 307 | //
tags around block-level tags.
454 | text = _HashHTMLBlocks(text);
455 | text = _FormParagraphs(text);
456 |
457 | return text;
458 | }
459 |
460 |
461 | var _RunSpanGamut = function(text) {
462 | //
463 | // These are all the transformations that occur *within* block-level
464 | // tags like paragraphs, headers, and list items.
465 | //
466 |
467 | text = _DoCodeSpans(text);
468 | text = _EscapeSpecialCharsWithinTagAttributes(text);
469 | text = _EncodeBackslashEscapes(text);
470 |
471 | // Process anchor and image tags. Images must come first,
472 | // because ![foo][f] looks like an anchor.
473 | text = _DoImages(text);
474 | text = _DoAnchors(text);
475 |
476 | // Make links out of things like ` Just type tags
1147 | //
1148 |
1149 | // Strip leading and trailing lines:
1150 | text = text.replace(/^\n+/g,"");
1151 | text = text.replace(/\n+$/g,"");
1152 |
1153 | var grafs = text.split(/\n{2,}/g);
1154 | var grafsOut = new Array();
1155 |
1156 | //
1157 | // Wrap tags.
1158 | //
1159 | var end = grafs.length;
1160 | for (var i=0; i ");
1171 | str += "
29 | * Usage:
47 | * Change log: Specifically, I've removed any keywords that can't precede a regexp
187 | * literal in a syntactically legal javascript program, and I've removed the
188 | * "in" keyword since it's not a keyword in many languages, and might be used
189 | * as a count of inches.
190 | *
191 | * The link a above does not accurately describe EcmaScript rules since
192 | * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
193 | * very well in practice.
194 | *
195 | * @private
196 | */
197 | var REGEXP_PRECEDER_PATTERN = function () {
198 | var preceders = [
199 | "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
200 | "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
201 | "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
202 | "<", "<<", "<<=", "<=", "=", "==", "===", ">",
203 | ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
204 | "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
205 | "||=", "~" /* handles =~ and !~ */,
206 | "break", "case", "continue", "delete",
207 | "do", "else", "finally", "instanceof",
208 | "return", "throw", "try", "typeof"
209 | ];
210 | var pattern = '(?:^^|[+-]';
211 | for (var i = 0; i < preceders.length; ++i) {
212 | pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
213 | }
214 | pattern += ')\\s*'; // matches at end, and matches empty string
215 | return pattern;
216 | // CAVEAT: this does not properly handle the case where a regular
217 | // expression immediately follows another since a regular expression may
218 | // have flags for case-sensitivity and the like. Having regexp tokens
219 | // adjacent is not valid in any language I'm aware of, so I'm punting.
220 | // TODO: maybe style special characters inside a regexp as punctuation.
221 | }();
222 |
223 | // Define regexps here so that the interpreter doesn't have to create an
224 | // object each time the function containing them is called.
225 | // The language spec requires a new object created even if you don't access
226 | // the $1 members.
227 | var pr_amp = /&/g;
228 | var pr_lt = //g;
230 | var pr_quot = /\"/g;
231 | /** like textToHtml but escapes double quotes to be attribute safe. */
232 | function attribToHtml(str) {
233 | return str.replace(pr_amp, '&')
234 | .replace(pr_lt, '<')
235 | .replace(pr_gt, '>')
236 | .replace(pr_quot, '"');
237 | }
238 |
239 | /** escapest html special characters to html. */
240 | function textToHtml(str) {
241 | return str.replace(pr_amp, '&')
242 | .replace(pr_lt, '<')
243 | .replace(pr_gt, '>');
244 | }
245 |
246 |
247 | var pr_ltEnt = /</g;
248 | var pr_gtEnt = />/g;
249 | var pr_aposEnt = /'/g;
250 | var pr_quotEnt = /"/g;
251 | var pr_ampEnt = /&/g;
252 | var pr_nbspEnt = / /g;
253 | /** unescapes html to plain text. */
254 | function htmlToText(html) {
255 | var pos = html.indexOf('&');
256 | if (pos < 0) { return html; }
257 | // Handle numeric entities specially. We can't use functional substitution
258 | // since that doesn't work in older versions of Safari.
259 | // These should be rare since most browsers convert them to normal chars.
260 | for (--pos; (pos = html.indexOf('', pos + 1)) >= 0;) {
261 | var end = html.indexOf(';', pos);
262 | if (end >= 0) {
263 | var num = html.substring(pos + 3, end);
264 | var radix = 10;
265 | if (num && num.charAt(0) === 'x') {
266 | num = num.substring(1);
267 | radix = 16;
268 | }
269 | var codePoint = parseInt(num, radix);
270 | if (!isNaN(codePoint)) {
271 | html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
272 | html.substring(end + 1));
273 | }
274 | }
275 | }
276 |
277 | return html.replace(pr_ltEnt, '<')
278 | .replace(pr_gtEnt, '>')
279 | .replace(pr_aposEnt, "'")
280 | .replace(pr_quotEnt, '"')
281 | .replace(pr_nbspEnt, ' ')
282 | .replace(pr_ampEnt, '&');
283 | }
284 |
285 | /** is the given node's innerHTML normally unescaped? */
286 | function isRawContent(node) {
287 | return 'XMP' === node.tagName;
288 | }
289 |
290 | var newlineRe = /[\r\n]/g;
291 | /**
292 | * Are newlines and adjacent spaces significant in the given node's innerHTML?
293 | */
294 | function isPreformatted(node, content) {
295 | // PRE means preformatted, and is a very common case, so don't create
296 | // unnecessary computed style objects.
297 | if ('PRE' === node.tagName) { return true; }
298 | if (!newlineRe.test(content)) { return true; } // Don't care
299 | var whitespace = '';
300 | // For disconnected nodes, IE has no currentStyle.
301 | if (node.currentStyle) {
302 | whitespace = node.currentStyle.whiteSpace;
303 | } else if (window.getComputedStyle) {
304 | // Firefox makes a best guess if node is disconnected whereas Safari
305 | // returns the empty string.
306 | whitespace = window.getComputedStyle(node, null).whiteSpace;
307 | }
308 | return !whitespace || whitespace === 'pre';
309 | }
310 |
311 | function normalizedHtml(node, out, opt_sortAttrs) {
312 | switch (node.nodeType) {
313 | case 1: // an element
314 | var name = node.tagName.toLowerCase();
315 |
316 | out.push('<', name);
317 | var attrs = node.attributes;
318 | var n = attrs.length;
319 | if (n) {
320 | if (opt_sortAttrs) {
321 | var sortedAttrs = [];
322 | for (var i = n; --i >= 0;) { sortedAttrs[i] = attrs[i]; }
323 | sortedAttrs.sort(function (a, b) {
324 | return (a.name < b.name) ? -1 : a.name === b.name ? 0 : 1;
325 | });
326 | attrs = sortedAttrs;
327 | }
328 | for (var i = 0; i < n; ++i) {
329 | var attr = attrs[i];
330 | if (!attr.specified) { continue; }
331 | out.push(' ', attr.name.toLowerCase(),
332 | '="', attribToHtml(attr.value), '"');
333 | }
334 | }
335 | out.push('>');
336 | for (var child = node.firstChild; child; child = child.nextSibling) {
337 | normalizedHtml(child, out, opt_sortAttrs);
338 | }
339 | if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
340 | out.push('<\/', name, '>');
341 | }
342 | break;
343 | case 3: case 4: // text
344 | out.push(textToHtml(node.nodeValue));
345 | break;
346 | }
347 | }
348 |
349 | /**
350 | * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
351 | * matches the union o the sets o strings matched d by the input RegExp.
352 | * Since it matches globally, if the input strings have a start-of-input
353 | * anchor (/^.../), it is ignored for the purposes of unioning.
354 | * @param {Array.
\n");
485 |
486 | return text;
487 | }
488 |
489 | var _EscapeSpecialCharsWithinTagAttributes = function(text) {
490 | //
491 | // Within tags -- meaning between < and > -- encode [\ ` * _] so they
492 | // don't conflict with their use in Markdown for code, italics and strong.
493 | //
494 |
495 | // Build a regex to find HTML tags and comments. See Friedl's
496 | // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
497 | var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi;
498 |
499 | text = text.replace(regex, function(wholeMatch) {
500 | var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
501 | tag = escapeCharacters(tag,"\\`*_");
502 | return tag;
503 | });
504 |
505 | return text;
506 | }
507 |
508 | var _DoAnchors = function(text) {
509 | //
510 | // Turn Markdown link shortcuts into XHTML tags.
511 | //
512 | //
513 | // First, handle reference-style links: [link text] [id]
514 | //
515 |
516 | /*
517 | text = text.replace(/
518 | ( // wrap whole match in $1
519 | \[
520 | (
521 | (?:
522 | \[[^\]]*\] // allow brackets nested one level
523 | |
524 | [^\[] // or anything else
525 | )*
526 | )
527 | \]
528 |
529 | [ ]? // one optional space
530 | (?:\n[ ]*)? // one optional newline followed by spaces
531 |
532 | \[
533 | (.*?) // id = $3
534 | \]
535 | )()()()() // pad remaining backreferences
536 | /g,_DoAnchors_callback);
537 | */
538 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
539 |
540 | //
541 | // Next, inline-style links: [link text](url "optional title")
542 | //
543 |
544 | /*
545 | text = text.replace(/
546 | ( // wrap whole match in $1
547 | \[
548 | (
549 | (?:
550 | \[[^\]]*\] // allow brackets nested one level
551 | |
552 | [^\[\]] // or anything else
553 | )
554 | )
555 | \]
556 | \( // literal paren
557 | [ \t]*
558 | () // no id, so leave $3 empty
559 | (.*?)>? // href = $4
560 | [ \t]*
561 | ( // $5
562 | (['"]) // quote char = $6
563 | (.*?) // Title = $7
564 | \6 // matching quote
565 | [ \t]* // ignore any spaces/tabs between closing quote and )
566 | )? // title is optional
567 | \)
568 | )
569 | /g,writeAnchorTag);
570 | */
571 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
572 |
573 | //
574 | // Last, handle reference-style shortcuts: [link text]
575 | // These must come last in case you've also got [link test][1]
576 | // or [link test](/foo)
577 | //
578 |
579 | /*
580 | text = text.replace(/
581 | ( // wrap whole match in $1
582 | \[
583 | ([^\[\]]+) // link text = $2; can't contain '[' or ']'
584 | \]
585 | )()()()()() // pad rest of backreferences
586 | /g, writeAnchorTag);
587 | */
588 | text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
589 |
590 | return text;
591 | }
592 |
593 | var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
594 | if (m7 == undefined) m7 = "";
595 | var whole_match = m1;
596 | var link_text = m2;
597 | var link_id = m3.toLowerCase();
598 | var url = m4;
599 | var title = m7;
600 |
601 | if (url == "") {
602 | if (link_id == "") {
603 | // lower-case and turn embedded newlines into spaces
604 | link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
605 | }
606 | url = "#"+link_id;
607 |
608 | if (g_urls[link_id] != undefined) {
609 | url = g_urls[link_id];
610 | if (g_titles[link_id] != undefined) {
611 | title = g_titles[link_id];
612 | }
613 | }
614 | else {
615 | if (whole_match.search(/\(\s*\)$/m)>-1) {
616 | // Special case for explicit empty url
617 | url = "";
618 | } else {
619 | return whole_match;
620 | }
621 | }
622 | }
623 |
624 | url = escapeCharacters(url,"*_");
625 | var result = "" + link_text + "";
634 |
635 | return result;
636 | }
637 |
638 |
639 | var _DoImages = function(text) {
640 | //
641 | // Turn Markdown image shortcuts into tags.
642 | //
643 |
644 | //
645 | // First, handle reference-style labeled images: ![alt text][id]
646 | //
647 |
648 | /*
649 | text = text.replace(/
650 | ( // wrap whole match in $1
651 | !\[
652 | (.*?) // alt text = $2
653 | \]
654 |
655 | [ ]? // one optional space
656 | (?:\n[ ]*)? // one optional newline followed by spaces
657 |
658 | \[
659 | (.*?) // id = $3
660 | \]
661 | )()()()() // pad rest of backreferences
662 | /g,writeImageTag);
663 | */
664 | text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
665 |
666 | //
667 | // Next, handle inline images: 
668 | // Don't forget: encode * and _
669 |
670 | /*
671 | text = text.replace(/
672 | ( // wrap whole match in $1
673 | !\[
674 | (.*?) // alt text = $2
675 | \]
676 | \s? // One optional whitespace character
677 | \( // literal paren
678 | [ \t]*
679 | () // no id, so leave $3 empty
680 | (\S+?)>? // src url = $4
681 | [ \t]*
682 | ( // $5
683 | (['"]) // quote char = $6
684 | (.*?) // title = $7
685 | \6 // matching quote
686 | [ \t]*
687 | )? // title is optional
688 | \)
689 | )
690 | /g,writeImageTag);
691 | */
692 | text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
693 |
694 | return text;
695 | }
696 |
697 | var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
698 | var whole_match = m1;
699 | var alt_text = m2;
700 | var link_id = m3.toLowerCase();
701 | var url = m4;
702 | var title = m7;
703 |
704 | if (!title) title = "";
705 |
706 | if (url == "") {
707 | if (link_id == "") {
708 | // lower-case and turn embedded newlines into spaces
709 | link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
710 | }
711 | url = "#"+link_id;
712 |
713 | if (g_urls[link_id] != undefined) {
714 | url = g_urls[link_id];
715 | if (g_titles[link_id] != undefined) {
716 | title = g_titles[link_id];
717 | }
718 | }
719 | else {
720 | return whole_match;
721 | }
722 | }
723 |
724 | alt_text = alt_text.replace(/"/g,""");
725 | url = escapeCharacters(url,"*_");
726 | var result = "
";
738 |
739 | return result;
740 | }
741 |
742 |
743 | var _DoHeaders = function(text) {
744 |
745 | // Setext-style headers:
746 | // Header 1
747 | // ========
748 | //
749 | // Header 2
750 | // --------
751 | //
752 | text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
753 | function(wholeMatch,m1){return hashBlock("
" + _RunSpanGamut(m1) + "
");});
754 |
755 | text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
756 | function(matchFound,m1){return hashBlock("" + _RunSpanGamut(m1) + "
");});
757 |
758 | // atx-style headers:
759 | // # Header 1
760 | // ## Header 2
761 | // ## Header 2 with closing hashes ##
762 | // ...
763 | // ###### Header 6
764 | //
765 |
766 | /*
767 | text = text.replace(/
768 | ^(\#{1,6}) // $1 = string of #'s
769 | [ \t]*
770 | (.+?) // $2 = Header text
771 | [ \t]*
772 | \#* // optional closing #'s (not counted)
773 | \n+
774 | /gm, function() {...});
775 | */
776 |
777 | text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
778 | function(wholeMatch,m1,m2) {
779 | var h_level = m1.length;
780 | return hashBlock("` blocks.
938 | //
939 |
940 | //
941 | // mk2: support code block
942 | // ```
943 | // console.log('markdown');
944 | // ```
945 | text = text.replace(/```\n*((.|\n)*?)```/g,
946 | function(wholeMatch, m1) {
947 | var codeblock = m1;
948 | codeblock = "
";
949 |
950 | return hashBlock(codeblock);
951 | }
952 | );
953 |
954 | /*
955 | text = text.replace(text,
956 | /(?:\n\n|^)
957 | ( // $1 = the code block -- one or more lines, starting with a space/tab
958 | (?:
959 | (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
960 | .*\n+
961 | )+
962 | )
963 | (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
964 | /g,function(){...});
965 | */
966 |
967 | // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
968 | text += "~0";
969 |
970 | text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
971 | function(wholeMatch,m1,m2) {
972 | var codeblock = m1;
973 | var nextChar = m2;
974 |
975 | codeblock = _EncodeCode( _Outdent(codeblock));
976 | codeblock = _Detab(codeblock);
977 | codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
978 | codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
979 |
980 | codeblock = "" + codeblock + "
";
981 |
982 | return hashBlock(codeblock) + nextChar;
983 | }
984 | );
985 |
986 | // attacklab: strip sentinel
987 | text = text.replace(/~0/,"");
988 |
989 | return text;
990 | }
991 |
992 | var hashBlock = function(text) {
993 | text = text.replace(/(^\n+|\n+$)/g,"");
994 | return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
995 | }
996 |
997 |
998 | var _DoCodeSpans = function(text) {
999 | //
1000 | // * Backtick quotes are used for " + codeblock + "\n
spans.
1001 | //
1002 | // * You can use multiple backticks as the delimiters if you want to
1003 | // include literal backticks in the code span. So, this input:
1004 | //
1005 | // Just type ``foo `bar` baz`` at the prompt.
1006 | //
1007 | // Will translate to:
1008 | //
1009 | //
foo `bar` baz
at the prompt.`bar`
...
1022 | //
1023 |
1024 | /*
1025 | text = text.replace(/
1026 | (^|[^\\]) // Character before opening ` can't be a backslash
1027 | (`+) // $2 = Opening run of `
1028 | ( // $3 = The code block
1029 | [^\r]*?
1030 | [^`] // attacklab: work around lack of lookbehind
1031 | )
1032 | \2 // Matching closer
1033 | (?!`)
1034 | /gm, function(){...});
1035 | */
1036 |
1037 | text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
1038 | function(wholeMatch,m1,m2,m3,m4) {
1039 | var c = m3;
1040 | c = c.replace(/^([ \t]*)/g,""); // leading whitespace
1041 | c = c.replace(/[ \t]*$/g,""); // trailing whitespace
1042 | c = _EncodeCode(c);
1043 | return m1+""+c+"
";
1044 | });
1045 |
1046 | return text;
1047 | }
1048 |
1049 |
1050 | var _EncodeCode = function(text) {
1051 | //
1052 | // Encode/escape certain characters inside Markdown code runs.
1053 | // The point is that in code, these characters are literals,
1054 | // and lose their special Markdown meanings.
1055 | //
1056 | // Encode all ampersands; HTML entities are not
1057 | // entities within a Markdown code span.
1058 | text = text.replace(/&/g,"&");
1059 |
1060 | // Do the angle bracket song and dance:
1061 | text = text.replace(//g,">");
1063 |
1064 | // Now, escape characters that are magic in Markdown:
1065 | text = escapeCharacters(text,"\*_{}[]\\",false);
1066 |
1067 | // jj the line above breaks this:
1068 | //---
1069 |
1070 | //* Item
1071 |
1072 | // 1. Subitem
1073 |
1074 | // special char: *
1075 | //---
1076 |
1077 | return text;
1078 | }
1079 |
1080 |
1081 | var _DoItalicsAndBold = function(text) {
1082 |
1083 | // must go first:
1084 | text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
1085 | "$2");
1086 |
1087 | text = text.replace(/(\w)_(\w)/g, "$1~E95E$2") // ** GFM ** "~E95E" == escaped "_"
1088 | text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
1089 | "$2");
1090 |
1091 | return text;
1092 | }
1093 |
1094 |
1095 | var _DoBlockQuotes = function(text) {
1096 |
1097 | /*
1098 | text = text.replace(/
1099 | ( // Wrap whole match in $1
1100 | (
1101 | ^[ \t]*>[ \t]? // '>' at the start of a line
1102 | .+\n // rest of the first line
1103 | (.+\n)* // subsequent consecutive lines
1104 | \n* // blanks
1105 | )+
1106 | )
1107 | /gm, function(){...});
1108 | */
1109 |
1110 | text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
1111 | function(wholeMatch,m1) {
1112 | var bq = m1;
1113 |
1114 | // attacklab: hack around Konqueror 3.5.4 bug:
1115 | // "----------bug".replace(/^-/g,"") == "bug"
1116 |
1117 | bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
1118 |
1119 | // attacklab: clean up hack
1120 | bq = bq.replace(/~0/g,"");
1121 |
1122 | bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
1123 | bq = _RunBlockGamut(bq); // recurse
1124 |
1125 | bq = bq.replace(/(^|\n)/g,"$1 ");
1126 | // These leading spaces screw with content, so we need to fix that:
1127 | bq = bq.replace(
1128 | /(\s*
[^\r]+?<\/pre>)/gm,
1129 | function(wholeMatch,m1) {
1130 | var pre = m1;
1131 | // attacklab: hack around Konqueror 3.5.4 bug:
1132 | pre = pre.replace(/^ /mg,"~0");
1133 | pre = pre.replace(/~0/g,"");
1134 | return pre;
1135 | });
1136 |
1137 | return hashBlock("
\n" + bq + "\n
");
1138 | });
1139 | return text;
1140 | }
1141 |
1142 |
1143 | var _FormParagraphs = function(text) {
1144 | //
1145 | // Params:
1146 | // $text - string to process with html
"); // ** GFM **
1170 | str = str.replace(/^([ \t]*)/g,"
30 | *
39 | * That's it. I wanted to keep the API as simple as possible, so there's no
40 | * need to specify which language the code is in, but if you wish, you can add
41 | * another class to the {@code } and {@code
} tags in your source with
34 | * {@code class=prettyprint.}
35 | * You can also use the (html deprecated) {@code
} or {@code
} element to specify the
42 | * language, as in {@code
}. Any class that
43 | * starts with "lang-" followed by a file extension, specifies the file type.
44 | * See the "lang-*.js" files in this directory for code that implements
45 | * per-language file handlers.
46 | *
48 | * cbeust, 2006/08/22
49 | *
50 | * Java annotations (start with "@") are now captured as literals ("lit")
51 | *
52 | * @requires console
53 | */
54 |
55 | // JSLint declarations
56 | /*global console, document, navigator, setTimeout, window */
57 |
58 | /**
59 | * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
60 | * UI events.
61 | * If set to {@code false}, {@code prettyPrint()} is synchronous.
62 | */
63 | window['PR_SHOULD_USE_CONTINUATION'] = true;
64 |
65 | /** the number of characters between tab columns */
66 | window['PR_TAB_WIDTH'] = 8;
67 |
68 | /** Walks the DOM returning a properly escaped version of innerHTML.
69 | * @param {Node} node
70 | * @param {Array.} and {@code
} tags in the DOM with
86 | * {@code class=prettyprint} and prettify them.
87 | * @param {Function?} opt_whenDone if specified, called when the last entry
88 | * has been finished.
89 | */
90 | = window['prettyPrint'] = void 0;
91 |
92 | /** browser detection. @extern @returns false if not IE, otherwise the major version. */
93 | window['_pr_isIE6'] = function () {
94 | var ieVersion = navigator && navigator.userAgent &&
95 | navigator.userAgent.match(/\bMSIE ([678])\./);
96 | ieVersion = ieVersion ? +ieVersion[1] : false;
97 | window['_pr_isIE6'] = function () { return ieVersion; };
98 | return ieVersion;
99 | };
100 |
101 |
102 | (function () {
103 | // Keyword lists for various languages.
104 | var FLOW_CONTROL_KEYWORDS =
105 | "break continue do else for if return while ";
106 | var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
107 | "double enum extern float goto int long register short signed sizeof " +
108 | "static struct switch typedef union unsigned void volatile ";
109 | var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
110 | "new operator private protected public this throw true try typeof ";
111 | var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
112 | "concept concept_map const_cast constexpr decltype " +
113 | "dynamic_cast explicit export friend inline late_check " +
114 | "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
115 | "template typeid typename using virtual wchar_t where ";
116 | var JAVA_KEYWORDS = COMMON_KEYWORDS +
117 | "abstract boolean byte extends final finally implements import " +
118 | "instanceof null native package strictfp super synchronized throws " +
119 | "transient ";
120 | var CSHARP_KEYWORDS = JAVA_KEYWORDS +
121 | "as base by checked decimal delegate descending dynamic event " +
122 | "fixed foreach from group implicit in interface internal into is lock " +
123 | "object out override orderby params partial readonly ref sbyte sealed " +
124 | "stackalloc string select uint ulong unchecked unsafe ushort var ";
125 | var COFFEE_KEYWORDS = "all and by catch class else extends false finally " +
126 | "for if in is isnt loop new no not null of off on or return super then " +
127 | "true try unless until when while yes ";
128 | var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
129 | "debugger eval export function get null set undefined var with " +
130 | "Infinity NaN ";
131 | var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
132 | "goto if import last local my next no our print package redo require " +
133 | "sub undef unless until use wantarray while BEGIN END ";
134 | var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
135 | "elif except exec finally from global import in is lambda " +
136 | "nonlocal not or pass print raise try with yield " +
137 | "False True None ";
138 | var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
139 | " defined elsif end ensure false in module next nil not or redo rescue " +
140 | "retry self super then true undef unless until when yield BEGIN END ";
141 | var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
142 | "function in local set then until ";
143 | var ALL_KEYWORDS = (
144 | CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
145 | PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
146 |
147 | // token style names. correspond to css classes
148 | /** token style for a string literal */
149 | var PR_STRING = 'str';
150 | /** token style for a keyword */
151 | var PR_KEYWORD = 'kwd';
152 | /** token style for a comment */
153 | var PR_COMMENT = 'com';
154 | /** token style for a type */
155 | var PR_TYPE = 'typ';
156 | /** token style for a literal value. e.g. 1, null, true. */
157 | var PR_LITERAL = 'lit';
158 | /** token style for a punctuation string. */
159 | var PR_PUNCTUATION = 'pun';
160 | /** token style for a punctuation string. */
161 | var PR_PLAIN = 'pln';
162 |
163 | /** token style for an sgml tag. */
164 | var PR_TAG = 'tag';
165 | /** token style for a markup declaration such as a DOCTYPE. */
166 | var PR_DECLARATION = 'dec';
167 | /** token style for embedded source. */
168 | var PR_SOURCE = 'src';
169 | /** token style for an sgml attribute name. */
170 | var PR_ATTRIB_NAME = 'atn';
171 | /** token style for an sgml attribute value. */
172 | var PR_ATTRIB_VALUE = 'atv';
173 |
174 | /**
175 | * A class that indicates a section of markup that is not code, e.g. to allow
176 | * embedding of line numbers within code listings.
177 | */
178 | var PR_NOCODE = 'nocode';
179 |
180 | /** A set of tokens that can precede a regular expression literal in
181 | * javascript.
182 | * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
183 | * list, but I've removed ones that might be problematic when seen in
184 | * languages that don't support regular expression literals.
185 | *
186 | *
tags are lexically significant so convert them to text.
697 | // This is undone later.
698 | sourceBuf.push('\n');
699 | ++sourceBufLen;
700 | } else {
701 | if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
702 | // A will start a section that should be
703 | // ignored. Continue walking the list until we see a matching end
704 | // tag.
705 | var name = match.match(pr_tagNameRe)[2];
706 | var depth = 1;
707 | var j;
708 | end_tag_loop:
709 | for (j = i + 1; j < n; ++j) {
710 | var name2 = matches[j].match(pr_tagNameRe);
711 | if (name2 && name2[2] === name) {
712 | if (name2[1] === '/') {
713 | if (--depth === 0) { break end_tag_loop; }
714 | } else {
715 | ++depth;
716 | }
717 | }
718 | }
719 | if (j < n) {
720 | extractedTags.push(
721 | sourceBufLen, matches.slice(i, j + 1).join(''));
722 | i = j;
723 | } else { // Ignore unclosed sections.
724 | extractedTags.push(sourceBufLen, match);
725 | }
726 | } else {
727 | extractedTags.push(sourceBufLen, match);
728 | }
729 | }
730 | } else {
731 | var literalText = htmlToText(match);
732 | sourceBuf.push(literalText);
733 | sourceBufLen += literalText.length;
734 | }
735 | }
736 | }
737 | return { source: sourceBuf.join(''), tags: extractedTags };
738 | }
739 |
740 | /** True if the given tag contains a class attribute with the nocode class. */
741 | function isNoCodeTag(tag) {
742 | return !!tag
743 | // First canonicalize the representation of attributes
744 | .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
745 | ' $1="$2$3$4"')
746 | // Then look for the attribute we want.
747 | .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
748 | }
749 |
750 | /**
751 | * Apply the given language handler to sourceCode and add the resulting
752 | * decorations to out.
753 | * @param {number} basePos the index of sourceCode within the chunk of source
754 | * whose decorations are already present on out.
755 | */
756 | function appendDecorations(basePos, sourceCode, langHandler, out) {
757 | if (!sourceCode) { return; }
758 | var job = {
759 | source: sourceCode,
760 | basePos: basePos
761 | };
762 | langHandler(job);
763 | out.push.apply(out, job.decorations);
764 | }
765 |
766 | /** Given triples of [style, pattern, context] returns a lexing function,
767 | * The lexing function interprets the patterns to find token boundaries and
768 | * returns a decoration list of the form
769 | * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
770 | * where index_n is an index into the sourceCode, and style_n is a style
771 | * constant like PR_PLAIN. index_n-1 <= index_n, and style_n-1 applies to
772 | * all characters in sourceCode[index_n-1:index_n].
773 | *
774 | * The stylePatterns is a list whose elements have the form
775 | * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
776 | *
777 | * Style is a style constant like PR_PLAIN, or can be a string of the
778 | * form 'lang-FOO', where FOO is a language extension describing the
779 | * language of the portion of the token in $1 after pattern executes.
780 | * E.g., if style is 'lang-lisp', and group 1 contains the text
781 | * '(hello (world))', then that portion of the token will be passed to the
782 | * registered lisp handler for formatting.
783 | * The text before and after group 1 will be restyled using this decorator
784 | * so decorators should take care that this doesn't result in infinite
785 | * recursion. For example, the HTML lexer rule for SCRIPT elements looks
786 | * something like ['lang-js', /<[s]cript>(.+?)<\/script>/]. This may match
787 | * '
13 |
14 |
31 |
32 |
33 |
34 |
50 | @<%- user.screen_name %>
51 |
52 | •
53 | 退出
54 | <% } else { %>
55 | •
56 | 登录
57 | <% } %>
58 |
60 | <%- config.site_name %>
61 |
62 |