# HG changeset patch
# User Edho Arief 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 @@
* } 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.
- *
- *
* {
* 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 @@
*
{ * 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 ors - // 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 ors + // 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; + }); + } })();