# 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 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;
+ });
+ }
})();