# HG changeset patch # User Edho Arief # Date 1368327908 -32400 # Node ID 183e8560a337b812a55d4071c489f1829583207b # Parent 56c4cd16d8492ac0b8b6552dc84e91ab8118233d Update prettify to 2013/03/04. diff -r 56c4cd16d849 -r 183e8560a337 vendor/assets/javascripts/prettify.js --- a/vendor/assets/javascripts/prettify.js Sun May 12 12:00:17 2013 +0900 +++ b/vendor/assets/javascripts/prettify.js Sun May 12 12:05:08 2013 +0900 @@ -53,7 +53,10 @@ */ // JSLint declarations -/*global console, document, navigator, setTimeout, window */ +/*global console, document, navigator, setTimeout, window, define */ + +/** @define {boolean} */ +var IN_GLOBAL_SCOPE = true; /** * Split {@code prettyPrint} into multiple timeouts so as not to interfere with @@ -62,33 +65,57 @@ */ window['PR_SHOULD_USE_CONTINUATION'] = true; +/** + * Pretty print a chunk of code. + * @param {string} sourceCodeHtml The HTML to pretty print. + * @param {string} opt_langExtension The language name to use. + * Typically, a filename extension like 'cpp' or 'java'. + * @param {number|boolean} opt_numberLines True to number lines, + * or the 1-indexed number of the first line in sourceCodeHtml. + * @return {string} code as html, but prettier + */ +var prettyPrintOne; +/** + * Find all the {@code
} and {@code } tags in the DOM with
+ * {@code class=prettyprint} and prettify them.
+ *
+ * @param {Function} opt_whenDone called when prettifying is done.
+ * @param {HTMLElement|HTMLDocument} opt_root an element or document
+ *   containing all the elements to pretty print.
+ *   Defaults to {@code document.body}.
+ */
+var prettyPrint;
+
+
 (function () {
+  var win = window;
   // Keyword lists for various languages.
   // We use things that coerce to strings to make them compact when minified
   // and to defeat aggressive optimizers that fold large string constants.
   var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
   var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," + 
-      "double,enum,extern,float,goto,int,long,register,short,signed,sizeof," +
-      "static,struct,switch,typedef,union,unsigned,void,volatile"];
+      "double,enum,extern,float,goto,inline,int,long,register,short,signed," +
+      "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
   var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
       "new,operator,private,protected,public,this,throw,true,try,typeof"];
   var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
-      "concept,concept_map,const_cast,constexpr,decltype," +
-      "dynamic_cast,explicit,export,friend,inline,late_check," +
-      "mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast," +
-      "template,typeid,typename,using,virtual,where"];
+      "concept,concept_map,const_cast,constexpr,decltype,delegate," +
+      "dynamic_cast,explicit,export,friend,generic,late_check," +
+      "mutable,namespace,nullptr,property,reinterpret_cast,static_assert," +
+      "static_cast,template,typeid,typename,using,virtual,where"];
   var JAVA_KEYWORDS = [COMMON_KEYWORDS,
-      "abstract,boolean,byte,extends,final,finally,implements,import," +
-      "instanceof,null,native,package,strictfp,super,synchronized,throws," +
-      "transient"];
+      "abstract,assert,boolean,byte,extends,final,finally,implements,import," +
+      "instanceof,interface,null,native,package,strictfp,super,synchronized," +
+      "throws,transient"];
   var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
       "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
-      "fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock," +
-      "object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed," +
-      "stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];
+      "fixed,foreach,from,group,implicit,in,internal,into,is,let," +
+      "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte," +
+      "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort," +
+      "var,virtual,where"];
   var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
       "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
-      "true,try,unless,until,when,while,yes";
+      "throw,true,try,unless,until,when,while,yes";
   var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
       "debugger,eval,export,function,get,null,set,undefined,var,with," +
       "Infinity,NaN"];
@@ -103,12 +130,15 @@
       "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
       "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
       "BEGIN,END"];
+   var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "as,assert,const,copy,drop," +
+      "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv," +
+      "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"];
   var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
       "function,in,local,set,then,until"];
   var ALL_KEYWORDS = [
-      CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS +
+      CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS,
       PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
-  var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;
+  var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
 
   // token style names.  correspond to css classes
   /**
@@ -142,7 +172,7 @@
    */
   var PR_PUNCTUATION = 'pun';
   /**
-   * token style for a punctuation string.
+   * token style for plain text.
    * @const
    */
   var PR_PLAIN = 'pln';
@@ -180,35 +210,34 @@
    */
   var PR_NOCODE = 'nocode';
 
-
-
-/**
- * A set of tokens that can precede a regular expression literal in
- * javascript
- * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
- * has the full list, but I've removed ones that might be problematic when
- * seen in languages that don't support regular expression literals.
- *
- * 

Specifically, I've removed any keywords that can't precede a regexp - * literal in a syntactically legal javascript program, and I've removed the - * "in" keyword since it's not a keyword in many languages, and might be used - * as a count of inches. - * - *

The link a above does not accurately describe EcmaScript rules since - * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works - * very well in practice. - * - * @private - * @const - */ -var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*'; - -// CAVEAT: this does not properly handle the case where a regular -// expression immediately follows another since a regular expression may -// have flags for case-sensitivity and the like. Having regexp tokens -// adjacent is not valid in any language I'm aware of, so I'm punting. -// TODO: maybe style special characters inside a regexp as punctuation. - + + + /** + * A set of tokens that can precede a regular expression literal in + * javascript + * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html + * has the full list, but I've removed ones that might be problematic when + * seen in languages that don't support regular expression literals. + * + *

Specifically, I've removed any keywords that can't precede a regexp + * literal in a syntactically legal javascript program, and I've removed the + * "in" keyword since it's not a keyword in many languages, and might be used + * as a count of inches. + * + *

The link above does not accurately describe EcmaScript rules since + * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works + * very well in practice. + * + * @private + * @const + */ + var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*'; + + // CAVEAT: this does not properly handle the case where a regular + // expression immediately follows another since a regular expression may + // have flags for case-sensitivity and the like. Having regexp tokens + // adjacent is not valid in any language I'm aware of, so I'm punting. + // TODO: maybe style special characters inside a regexp as punctuation. /** * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally @@ -267,10 +296,8 @@ return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16); } var ch = String.fromCharCode(charCode); - if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') { - ch = '\\' + ch; - } - return ch; + return (ch === '\\' || ch === '-' || ch === ']' || ch === '^') + ? "\\" + ch : ch; } function caseFoldCharset(charSet) { @@ -284,13 +311,16 @@ + '|-' + '|[^-\\\\]', 'g')); - var groups = []; var ranges = []; var inverse = charsetParts[0] === '^'; + + var out = ['[']; + if (inverse) { out.push('^'); } + for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) { var p = charsetParts[i]; if (/\\[bdsw]/i.test(p)) { // Don't muck with named groups. - groups.push(p); + out.push(p); } else { var start = decodeEscape(p); var end; @@ -320,7 +350,7 @@ // -> [[1, 12], [14, 14], [16, 17]] ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); }); var consolidatedRanges = []; - var lastRange = [NaN, NaN]; + var lastRange = []; for (var i = 0; i < ranges.length; ++i) { var range = ranges[i]; if (range[0] <= lastRange[1] + 1) { @@ -330,9 +360,6 @@ } } - var out = ['[']; - if (inverse) { out.push('^'); } - out.push.apply(out, groups); for (var i = 0; i < consolidatedRanges.length; ++i) { var range = consolidatedRanges[i]; out.push(encodeEscape(range[0])); @@ -358,7 +385,7 @@ + '|\\\\[0-9]+' // a back-reference or octal escape + '|\\\\[^ux0-9]' // other escape sequence + '|\\(\\?[:!=]' // start of a non-capturing group - + '|[\\(\\)\\^]' // start/emd of a group, or line start + + '|[\\(\\)\\^]' // start/end of a group, or line start + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters + ')', 'g')); @@ -378,8 +405,15 @@ ++groupIndex; } else if ('\\' === p.charAt(0)) { var decimalValue = +p.substring(1); - if (decimalValue && decimalValue <= groupIndex) { - capturedGroups[decimalValue] = -1; + if (decimalValue) { + if (decimalValue <= groupIndex) { + capturedGroups[decimalValue] = -1; + } else { + // Replace with an unambiguous escape sequence so that + // an octal escape sequence does not turn into a backreference + // to a capturing group from an earlier regex. + parts[i] = encodeEscape(decimalValue); + } } } } @@ -395,20 +429,20 @@ var p = parts[i]; if (p === '(') { ++groupIndex; - if (capturedGroups[groupIndex] === undefined) { + if (!capturedGroups[groupIndex]) { parts[i] = '(?:'; } } else if ('\\' === p.charAt(0)) { var decimalValue = +p.substring(1); if (decimalValue && decimalValue <= groupIndex) { - parts[i] = '\\' + capturedGroups[groupIndex]; + parts[i] = '\\' + capturedGroups[decimalValue]; } } } // Remove any prefix anchors so that the output will match anywhere. // ^^ really does mean an anchored match though. - for (var i = 0, groupIndex = 0; i < n; ++i) { + for (var i = 0; i < n; ++i) { if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; } } @@ -446,7 +480,6 @@ return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g'); } - /** * Split markup into a string of source code and an array mapping ranges in * that string to the text nodes in which they appear. @@ -470,8 +503,8 @@ *

    * {
    *   sourceCode: "print 'Hello '\n  + 'World';",
-   *   //                 1         2
-   *   //       012345678901234 5678901234567
+   *   //                     1          2
+   *   //           012345678901234 5678901234567
    *   spans: [0, #1, 6, #2, 14, #3, 15, #4]
    * }
    * 
@@ -488,9 +521,11 @@ *

* * @param {Node} node an HTML DOM subtree containing source-code. + * @param {boolean} isPreformatted true if white-space in text nodes should + * be considered significant. * @return {Object} source code and the text nodes in which they occur. */ - function extractSourceSpans(node) { + function extractSourceSpans(node, isPreformatted) { var nocode = /(?:^|\s)nocode(?:\s|$)/; var chunks = []; @@ -498,44 +533,33 @@ var spans = []; var k = 0; - var whitespace; - if (node.currentStyle) { - whitespace = node.currentStyle.whiteSpace; - } else if (window.getComputedStyle) { - whitespace = document.defaultView.getComputedStyle(node, null) - .getPropertyValue('white-space'); - } - var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3); - function walk(node) { - switch (node.nodeType) { - case 1: // Element - if (nocode.test(node.className)) { return; } - for (var child = node.firstChild; child; child = child.nextSibling) { - walk(child); + var type = node.nodeType; + if (type == 1) { // Element + if (nocode.test(node.className)) { return; } + for (var child = node.firstChild; child; child = child.nextSibling) { + walk(child); + } + var nodeName = node.nodeName.toLowerCase(); + if ('br' === nodeName || 'li' === nodeName) { + chunks[k] = '\n'; + spans[k << 1] = length++; + spans[(k++ << 1) | 1] = node; + } + } else if (type == 3 || type == 4) { // Text + var text = node.nodeValue; + if (text.length) { + if (!isPreformatted) { + text = text.replace(/[ \t\r\n]+/g, ' '); + } else { + text = text.replace(/\r\n?/g, '\n'); // Normalize newlines. } - var nodeName = node.nodeName; - if ('BR' === nodeName || 'LI' === nodeName) { - chunks[k] = '\n'; - spans[k << 1] = length++; - spans[(k++ << 1) | 1] = node; - } - break; - case 3: case 4: // Text - var text = node.nodeValue; - if (text.length) { - if (!isPreformatted) { - text = text.replace(/[ \t\r\n]+/g, ' '); - } else { - text = text.replace(/\r\n?/g, '\n'); // Normalize newlines. - } - // TODO: handle tabs here? - chunks[k] = text; - spans[k << 1] = length; - length += text.length; - spans[(k++ << 1) | 1] = node; - } - break; + // TODO: handle tabs here? + chunks[k] = text; + spans[k << 1] = length; + length += text.length; + spans[(k++ << 1) | 1] = node; + } } } @@ -547,7 +571,6 @@ }; } - /** * Apply the given language handler to sourceCode and add the resulting * decorations to out. @@ -812,12 +835,13 @@ } else { // Stop C preprocessor declarations at an unclosed open comment shortcutStylePatterns.push( - [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/, + [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/, null, '#']); } + // #include fallthroughStylePatterns.push( [PR_STRING, - /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/, + /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/, null]); } else { shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']); @@ -828,7 +852,18 @@ fallthroughStylePatterns.push( [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]); } - if (options['regexLiterals']) { + var regexLiterals = options['regexLiterals']; + if (regexLiterals) { + /** + * @const + */ + var regexExcls = regexLiterals > 1 + ? '' // Multiline regex literals + : '\n\r'; + /** + * @const + */ + var regexAny = regexExcls ? '.' : '[\\S\\s]'; /** * @const */ @@ -836,18 +871,19 @@ // A regular expression literal starts with a slash that is // not followed by * or / so that it is not confused with // comments. - '/(?=[^/*])' + '/(?=[^/*' + regexExcls + '])' // and then contains any number of raw characters, - + '(?:[^/\\x5B\\x5C]' + + '(?:[^/\\x5B\\x5C' + regexExcls + ']' // escape sequences (\x5C), - + '|\\x5C[\\s\\S]' + + '|\\x5C' + regexAny // or non-nesting character sets (\x5B\x5D); - + '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+' + + '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']' + + '|\\x5C' + regexAny + ')*(?:\\x5D|$))+' // finally closed by a /. + '/'); fallthroughStylePatterns.push( ['lang-regex', - new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')') + RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')') ]); } @@ -865,6 +901,48 @@ } shortcutStylePatterns.push([PR_PLAIN, /^\s+/, null, ' \r\n\t\xA0']); + + var punctuation = + // The Bash man page says + + // A word is a sequence of characters considered as a single + // unit by GRUB. Words are separated by metacharacters, + // which are the following plus space, tab, and newline: { } + // | & $ ; < > + // ... + + // A word beginning with # causes that word and all remaining + // characters on that line to be ignored. + + // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a + // comment but empirically + // $ echo {#} + // {#} + // $ echo \$# + // $# + // $ echo }# + // }# + + // so /(?:^|[|&;<>\s])/ is more appropriate. + + // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3 + // suggests that this definition is compatible with a + // default mode that tries to use a single token definition + // to recognize both bash/python style comments and C + // preprocessor directives. + + // This definition of punctuation does not include # in the list of + // follow-on exclusions, so # will not be broken before if preceeded + // by a punctuation character. We could try to exclude # after + // [|&;<>] but that doesn't seem to cause many major problems. + // If that does turn out to be a problem, we should change the below + // when hc is truthy to include # in the run of punctuation characters + // only when not followint [|&;<>]. + '^.[^\\s\\w.$@\'"`/\\\\]*'; + if (options['regexLiterals']) { + punctuation += '(?!\s*\/)'; + } + fallthroughStylePatterns.push( // TODO(mikesamuel): recognize non-latin letters and numerals in idents [PR_LITERAL, /^@[a-z_$][a-z_$@0-9]*/i, null], @@ -883,9 +961,10 @@ // with an optional modifier like UL for unsigned long + '[a-z]*', 'i'), null, '0123456789'], - // Don't treat escaped quotes in bash as starting strings. See issue 144. + // Don't treat escaped quotes in bash as starting strings. + // See issue 144. [PR_PLAIN, /^\\[\s\S]?/, null], - [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#\\]*/, null]); + [PR_PUNCTUATION, new RegExp(punctuation), null]); return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns); } @@ -906,25 +985,16 @@ * HTMLOListElement, and each line is moved into a separate list item. * This requires cloning elements, so the input might not have unique * IDs after numbering. + * @param {boolean} isPreformatted true iff white-space in text nodes should + * be treated as significant. */ - function numberLines(node, opt_startLineNum) { + function numberLines(node, opt_startLineNum, isPreformatted) { var nocode = /(?:^|\s)nocode(?:\s|$)/; var lineBreak = /\r\n?|\n/; var document = node.ownerDocument; - var whitespace; - if (node.currentStyle) { - whitespace = node.currentStyle.whiteSpace; - } else if (window.getComputedStyle) { - whitespace = document.defaultView.getComputedStyle(node, null) - .getPropertyValue('white-space'); - } - // If it's preformatted, then we need to split lines on line breaks - // in addition to
s. - var isPreformatted = whitespace && 'pre' === whitespace.substring(0, 3); - - var li = document.createElement('LI'); + var li = document.createElement('li'); while (node.firstChild) { li.appendChild(node.firstChild); } @@ -933,42 +1003,37 @@ var listItems = [li]; function walk(node) { - switch (node.nodeType) { - case 1: // Element - if (nocode.test(node.className)) { break; } - if ('BR' === node.nodeName) { - breakAfter(node); - // Discard the
since it is now flush against a . - if (node.parentNode) { - node.parentNode.removeChild(node); - } - } else { - for (var child = node.firstChild; child; child = child.nextSibling) { - walk(child); - } + var type = node.nodeType; + if (type == 1 && !nocode.test(node.className)) { // Element + if ('br' === node.nodeName) { + breakAfter(node); + // Discard the
since it is now flush against a . + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } else { + for (var child = node.firstChild; child; child = child.nextSibling) { + walk(child); } - break; - case 3: case 4: // Text - if (isPreformatted) { - var text = node.nodeValue; - var match = text.match(lineBreak); - if (match) { - var firstLine = text.substring(0, match.index); - node.nodeValue = firstLine; - var tail = text.substring(match.index + match[0].length); - if (tail) { - var parent = node.parentNode; - parent.insertBefore( - document.createTextNode(tail), node.nextSibling); - } - breakAfter(node); - if (!firstLine) { - // Don't leave blank text nodes in the DOM. - node.parentNode.removeChild(node); - } - } + } + } else if ((type == 3 || type == 4) && isPreformatted) { // Text + var text = node.nodeValue; + var match = text.match(lineBreak); + if (match) { + var firstLine = text.substring(0, match.index); + node.nodeValue = firstLine; + var tail = text.substring(match.index + match[0].length); + if (tail) { + var parent = node.parentNode; + parent.insertBefore( + document.createTextNode(tail), node.nextSibling); } - break; + breakAfter(node); + if (!firstLine) { + // Don't leave blank text nodes in the DOM. + node.parentNode.removeChild(node); + } + } } } @@ -1028,7 +1093,7 @@ listItems[0].setAttribute('value', opt_startLineNum); } - var ol = document.createElement('OL'); + var ol = document.createElement('ol'); ol.className = 'linenums'; var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0; for (var i = 0, n = listItems.length; i < n; ++i) { @@ -1045,12 +1110,12 @@ node.appendChild(ol); } - /** * Breaks {@code job.sourceCode} around style boundaries in * {@code job.decorations} and modifies {@code job.sourceNode} in place. * @param {Object} job like
{
    *    sourceCode: {string} source as plain text,
+   *    sourceNode: {HTMLElement} the element containing the source,
    *    spans: {Array.} alternating span start indices into source
    *       and the text node or element (e.g. {@code 
}) corresponding to that * span. @@ -1060,7 +1125,8 @@ * @private */ function recombineTagsAndDecorations(job) { - var isIE = /\bMSIE\b/.test(navigator.userAgent); + var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent); + isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8; var newlineRe = /\n/g; var source = job.sourceCode; @@ -1108,54 +1174,66 @@ nDecorations = decorations.length = decPos; - var decoration = null; - while (spanIndex < nSpans) { - var spanStart = spans[spanIndex]; - var spanEnd = spans[spanIndex + 2] || sourceLength; + var sourceNode = job.sourceNode; + var oldDisplay; + if (sourceNode) { + oldDisplay = sourceNode.style.display; + sourceNode.style.display = 'none'; + } + try { + var decoration = null; + while (spanIndex < nSpans) { + var spanStart = spans[spanIndex]; + var spanEnd = spans[spanIndex + 2] || sourceLength; - var decStart = decorations[decorationIndex]; - var decEnd = decorations[decorationIndex + 2] || sourceLength; + var decEnd = decorations[decorationIndex + 2] || sourceLength; - var end = Math.min(spanEnd, decEnd); + var end = Math.min(spanEnd, decEnd); - var textNode = spans[spanIndex + 1]; - var styledText; - if (textNode.nodeType !== 1 // Don't muck with
s or
  • s - // Don't introduce spans around empty text nodes. - && (styledText = source.substring(sourceIndex, end))) { - // This may seem bizarre, and it is. Emitting LF on IE causes the - // code to display with spaces instead of line breaks. - // Emitting Windows standard issue linebreaks (CRLF) causes a blank - // space to appear at the beginning of every line but the first. - // Emitting an old Mac OS 9 line separator makes everything spiffy. - if (isIE) { styledText = styledText.replace(newlineRe, '\r'); } - textNode.nodeValue = styledText; - var document = textNode.ownerDocument; - var span = document.createElement('SPAN'); - span.className = decorations[decorationIndex + 1]; - var parentNode = textNode.parentNode; - parentNode.replaceChild(span, textNode); - span.appendChild(textNode); - if (sourceIndex < spanEnd) { // Split off a text node. - spans[spanIndex + 1] = textNode - // TODO: Possibly optimize by using '' if there's no flicker. - = document.createTextNode(source.substring(end, spanEnd)); - parentNode.insertBefore(textNode, span.nextSibling); + var textNode = spans[spanIndex + 1]; + var styledText; + if (textNode.nodeType !== 1 // Don't muck with
    s or
  • s + // Don't introduce spans around empty text nodes. + && (styledText = source.substring(sourceIndex, end))) { + // This may seem bizarre, and it is. Emitting LF on IE causes the + // code to display with spaces instead of line breaks. + // Emitting Windows standard issue linebreaks (CRLF) causes a blank + // space to appear at the beginning of every line but the first. + // Emitting an old Mac OS 9 line separator makes everything spiffy. + if (isIE8OrEarlier) { + styledText = styledText.replace(newlineRe, '\r'); + } + textNode.nodeValue = styledText; + var document = textNode.ownerDocument; + var span = document.createElement('span'); + span.className = decorations[decorationIndex + 1]; + var parentNode = textNode.parentNode; + parentNode.replaceChild(span, textNode); + span.appendChild(textNode); + if (sourceIndex < spanEnd) { // Split off a text node. + spans[spanIndex + 1] = textNode + // TODO: Possibly optimize by using '' if there's no flicker. + = document.createTextNode(source.substring(end, spanEnd)); + parentNode.insertBefore(textNode, span.nextSibling); + } + } + + sourceIndex = end; + + if (sourceIndex >= spanEnd) { + spanIndex += 2; + } + if (sourceIndex >= decEnd) { + decorationIndex += 2; } } - - sourceIndex = end; - - if (sourceIndex >= spanEnd) { - spanIndex += 2; - } - if (sourceIndex >= decEnd) { - decorationIndex += 2; + } finally { + if (sourceNode) { + sourceNode.style.display = oldDisplay; } } } - /** Maps language-specific file extensions to handlers. */ var langHandlerRegistry = {}; /** Register a language handler for the given file extensions. @@ -1179,7 +1257,7 @@ var ext = fileExtensions[i]; if (!langHandlerRegistry.hasOwnProperty(ext)) { langHandlerRegistry[ext] = handler; - } else if (window['console']) { + } else if (win['console']) { console['warn']('cannot override language handler %s', ext); } } @@ -1259,30 +1337,30 @@ 'keywords': SH_KEYWORDS, 'hashComments': true, 'multiLineStrings': true - }), ['bsh', 'csh', 'sh']); + }), ['bash', 'bsh', 'csh', 'sh']); registerLangHandler(sourceDecorator({ 'keywords': PYTHON_KEYWORDS, 'hashComments': true, 'multiLineStrings': true, 'tripleQuotedStrings': true - }), ['cv', 'py']); + }), ['cv', 'py', 'python']); registerLangHandler(sourceDecorator({ 'keywords': PERL_KEYWORDS, 'hashComments': true, 'multiLineStrings': true, - 'regexLiterals': true + 'regexLiterals': 2 // multiline regex literals }), ['perl', 'pl', 'pm']); registerLangHandler(sourceDecorator({ 'keywords': RUBY_KEYWORDS, 'hashComments': true, 'multiLineStrings': true, 'regexLiterals': true - }), ['rb']); + }), ['rb', 'ruby']); registerLangHandler(sourceDecorator({ 'keywords': JSCRIPT_KEYWORDS, 'cStyleComments': true, 'regexLiterals': true - }), ['js']); + }), ['javascript', 'js']); registerLangHandler(sourceDecorator({ 'keywords': COFFEE_KEYWORDS, 'hashComments': 3, // ### style block comments @@ -1291,14 +1369,20 @@ 'tripleQuotedStrings': true, 'regexLiterals': true }), ['coffee']); - registerLangHandler(createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']); + registerLangHandler(sourceDecorator({ + 'keywords': RUST_KEYWORDS, + 'cStyleComments': true, + 'multilineStrings': true + }), ['rc', 'rs', 'rust']); + registerLangHandler( + createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']); function applyDecorator(job) { var opt_langExtension = job.langExtension; try { // Extract tags, and convert the source code to plain text. - var sourceAndSpans = extractSourceSpans(job.sourceNode); + var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre); /** Plain text. @type {string} */ var source = sourceAndSpans.sourceCode; job.sourceCode = source; @@ -1312,40 +1396,58 @@ // modifying the sourceNode in place. recombineTagsAndDecorations(job); } catch (e) { - if ('console' in window) { - console['log'](e && e['stack'] ? e['stack'] : e); + if (win['console']) { + console['log'](e && e['stack'] || e); } } } /** + * Pretty print a chunk of code. * @param sourceCodeHtml {string} The HTML to pretty print. * @param opt_langExtension {string} The language name to use. * Typically, a filename extension like 'cpp' or 'java'. * @param opt_numberLines {number|boolean} True to number lines, * or the 1-indexed number of the first line in sourceCodeHtml. */ - function prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) { - var container = document.createElement('PRE'); + function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) { + var container = document.createElement('div'); // This could cause images to load and onload listeners to fire. // E.g. . // We assume that the inner HTML is from a trusted source. - container.innerHTML = sourceCodeHtml; + // The pre-tag is required for IE8 which strips newlines from innerHTML + // when it is injected into a
     tag.
    +    // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
    +    // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
    +    container.innerHTML = '
    ' + sourceCodeHtml + '
    '; + container = container.firstChild; if (opt_numberLines) { - numberLines(container, opt_numberLines); + numberLines(container, opt_numberLines, true); } var job = { langExtension: opt_langExtension, numberLines: opt_numberLines, - sourceNode: container + sourceNode: container, + pre: 1 }; applyDecorator(job); return container.innerHTML; } - function prettyPrint(opt_whenDone) { - function byTagName(tn) { return document.getElementsByTagName(tn); } + /** + * Find all the {@code
    } and {@code } tags in the DOM with
    +    * {@code class=prettyprint} and prettify them.
    +    *
    +    * @param {Function} opt_whenDone called when prettifying is done.
    +    * @param {HTMLElement|HTMLDocument} opt_root an element or document
    +    *   containing all the elements to pretty print.
    +    *   Defaults to {@code document.body}.
    +    */
    +  function $prettyPrint(opt_whenDone, opt_root) {
    +    var root = opt_root || document.body;
    +    var doc = root.ownerDocument || document;
    +    function byTagName(tn) { return root.getElementsByTagName(tn); }
         // fetch a list of nodes to rewrite
         var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
         var elements = [];
    @@ -1368,59 +1470,124 @@
     
         var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
         var prettyPrintRe = /\bprettyprint\b/;
    +    var prettyPrintedRe = /\bprettyprinted\b/;
    +    var preformattedTagNameRe = /pre|xmp/i;
    +    var codeRe = /^code$/i;
    +    var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
    +    var EMPTY = {};
     
         function doWork() {
    -      var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
    +      var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
                          clock['now']() + 250 /* ms */ :
                          Infinity);
           for (; k < elements.length && clock['now']() < endTime; k++) {
             var cs = elements[k];
    +
    +        // Look for a preceding comment like
    +        // 
    +        var attrs = EMPTY;
    +        {
    +          for (var preceder = cs; (preceder = preceder.previousSibling);) {
    +            var nt = preceder.nodeType;
    +            //  is parsed by HTML 5 to a comment node (8)
    +            // like , but in XML is a processing instruction
    +            var value = (nt === 7 || nt === 8) && preceder.nodeValue;
    +            if (value
    +                ? !/^\??prettify\b/.test(value)
    +                : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
    +              // Skip over white-space text nodes but not others.
    +              break;
    +            }
    +            if (value) {
    +              attrs = {};
    +              value.replace(
    +                  /\b(\w+)=([\w:.%+-]+)/g,
    +                function (_, name, value) { attrs[name] = value; });
    +              break;
    +            }
    +          }
    +        }
    +
             var className = cs.className;
    -        if (className.indexOf('prettyprint') >= 0) {
    -          // If the classes includes a language extensions, use it.
    -          // Language extensions can be specified like
    -          //     
    -          // the language extension "cpp" is used to find a language handler as
    -          // passed to PR.registerLangHandler.
    -          // HTML5 recommends that a language be specified using "language-"
    -          // as the prefix instead.  Google Code Prettify supports both.
    -          // http://dev.w3.org/html5/spec-author-view/the-code-element.html
    -          var langExtension = className.match(langExtensionRe);
    -          // Support 
    
    -          var wrapper;
    -          if (!langExtension && (wrapper = childContentWrapper(cs))
    -              && "CODE" === wrapper.tagName) {
    -            langExtension = wrapper.className.match(langExtensionRe);
    -          }
    -
    -          if (langExtension) {
    -            langExtension = langExtension[1];
    -          }
    +        if ((attrs !== EMPTY || prettyPrintRe.test(className))
    +            // Don't redo this if we've already done it.
    +            // This allows recalling pretty print to just prettyprint elements
    +            // that have been added to the page since last call.
    +            && !prettyPrintedRe.test(className)) {
     
               // make sure this is not nested in an already prettified element
               var nested = false;
               for (var p = cs.parentNode; p; p = p.parentNode) {
    -            if ((p.tagName === 'pre' || p.tagName === 'code' ||
    -                 p.tagName === 'xmp') &&
    -                p.className && p.className.indexOf('prettyprint') >= 0) {
    +            var tn = p.tagName;
    +            if (preCodeXmpRe.test(tn)
    +                && p.className && prettyPrintRe.test(p.className)) {
                   nested = true;
                   break;
                 }
               }
               if (!nested) {
    +            // Mark done.  If we fail to prettyprint for whatever reason,
    +            // we shouldn't try again.
    +            cs.className += ' prettyprinted';
    +
    +            // If the classes includes a language extensions, use it.
    +            // Language extensions can be specified like
    +            //     
    +            // the language extension "cpp" is used to find a language handler
    +            // as passed to PR.registerLangHandler.
    +            // HTML5 recommends that a language be specified using "language-"
    +            // as the prefix instead.  Google Code Prettify supports both.
    +            // http://dev.w3.org/html5/spec-author-view/the-code-element.html
    +            var langExtension = attrs['lang'];
    +            if (!langExtension) {
    +              langExtension = className.match(langExtensionRe);
    +              // Support 
    
    +              var wrapper;
    +              if (!langExtension && (wrapper = childContentWrapper(cs))
    +                  && codeRe.test(wrapper.tagName)) {
    +                langExtension = wrapper.className.match(langExtensionRe);
    +              }
    +
    +              if (langExtension) { langExtension = langExtension[1]; }
    +            }
    +
    +            var preformatted;
    +            if (preformattedTagNameRe.test(cs.tagName)) {
    +              preformatted = 1;
    +            } else {
    +              var currentStyle = cs['currentStyle'];
    +              var defaultView = doc.defaultView;
    +              var whitespace = (
    +                  currentStyle
    +                  ? currentStyle['whiteSpace']
    +                  : (defaultView
    +                     && defaultView.getComputedStyle)
    +                  ? defaultView.getComputedStyle(cs, null)
    +                  .getPropertyValue('white-space')
    +                  : 0);
    +              preformatted = whitespace
    +                  && 'pre' === whitespace.substring(0, 3);
    +            }
    +
                 // Look for a class like linenums or linenums: where  is the
                 // 1-indexed number of the first line.
    -            var lineNums = cs.className.match(/\blinenums\b(?::(\d+))?/);
    -            lineNums = lineNums
    -                  ? lineNums[1] && lineNums[1].length ? +lineNums[1] : true
    -                  : false;
    -            if (lineNums) { numberLines(cs, lineNums); }
    +            var lineNums = attrs['linenums'];
    +            if (!(lineNums = lineNums === 'true' || +lineNums)) {
    +              lineNums = className.match(/\blinenums\b(?::(\d+))?/);
    +              lineNums =
    +                lineNums
    +                ? lineNums[1] && lineNums[1].length
    +                  ? +lineNums[1] : true
    +                : false;
    +            }
    +            if (lineNums) { numberLines(cs, lineNums, preformatted); }
     
                 // do the pretty printing
                 prettyPrintingJob = {
                   langExtension: langExtension,
                   sourceNode: cs,
    -              numberLines: lineNums
    +              numberLines: lineNums,
    +              pre: preformatted
                 };
                 applyDecorator(prettyPrintingJob);
               }
    @@ -1429,7 +1596,7 @@
           if (k < elements.length) {
             // finish up in a continuation
             setTimeout(doWork, 250);
    -      } else if (opt_whenDone) {
    +      } else if ('function' === typeof opt_whenDone) {
             opt_whenDone();
           }
         }
    @@ -1437,26 +1604,11 @@
         doWork();
       }
     
    -   /**
    -    * Find all the {@code 
    } and {@code } tags in the DOM with
    -    * {@code class=prettyprint} and prettify them.
    -    *
    -    * @param {Function?} opt_whenDone if specified, called when the last entry
    -    *     has been finished.
    -    */
    -  window['prettyPrintOne'] = prettyPrintOne;
    -   /**
    -    * Pretty print a chunk of code.
    -    *
    -    * @param {string} sourceCodeHtml code as html
    -    * @return {string} code as html, but prettier
    -    */
    -  window['prettyPrint'] = prettyPrint;
    -   /**
    -    * Contains functions for creating and registering new language handlers.
    -    * @type {Object}
    -    */
    -  window['PR'] = {
    +  /**
    +   * Contains functions for creating and registering new language handlers.
    +   * @type {Object}
    +   */
    +  var PR = win['PR'] = {
             'createSimpleLexer': createSimpleLexer,
             'registerLangHandler': registerLangHandler,
             'sourceDecorator': sourceDecorator,
    @@ -1472,6 +1624,32 @@
             'PR_SOURCE': PR_SOURCE,
             'PR_STRING': PR_STRING,
             'PR_TAG': PR_TAG,
    -        'PR_TYPE': PR_TYPE
    +        'PR_TYPE': PR_TYPE,
    +        'prettyPrintOne':
    +           IN_GLOBAL_SCOPE
    +             ? (win['prettyPrintOne'] = $prettyPrintOne)
    +             : (prettyPrintOne = $prettyPrintOne),
    +        'prettyPrint': prettyPrint =
    +           IN_GLOBAL_SCOPE
    +             ? (win['prettyPrint'] = $prettyPrint)
    +             : (prettyPrint = $prettyPrint)
           };
    +
    +  // Make PR available via the Asynchronous Module Definition (AMD) API.
    +  // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
    +  // The Asynchronous Module Definition (AMD) API specifies a
    +  // mechanism for defining modules such that the module and its
    +  // dependencies can be asynchronously loaded.
    +  // ...
    +  // To allow a clear indicator that a global define function (as
    +  // needed for script src browser loading) conforms to the AMD API,
    +  // any global define function SHOULD have a property called "amd"
    +  // whose value is an object. This helps avoid conflict with any
    +  // other existing JavaScript code that could have defined a define()
    +  // function that does not conform to the AMD API.
    +  if (typeof define === "function" && define['amd']) {
    +    define("google-code-prettify", [], function () {
    +      return PR; 
    +    });
    +  }
     })();