// Copyright (C) 2006 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License.
/** *Split{@codeprettyPrint}intomultipletimeoutssoasnottointerferewith *UIevents. *Ifsetto{@codefalse},{@codeprettyPrint()}issynchronous.
*/ var PR_SHOULD_USE_CONTINUATION = true;
/** the number of characters between tab columns */ var PR_TAB_WIDTH = 8;
/** Walks the DOM returning a properly escaped version of innerHTML. *@param{Node}node *@param{Array.<string>}outoutputbufferthatreceiveschunksofHTML.
*/ var PR_normalizedHtml;
/** Contains functions for creating and registering new language handlers. *@type{Object}
*/ var PR;
/** Pretty print a chunk of code. * *@param{string}sourceCodeHtmlcodeashtml *@return{string}codeashtml,butprettier
*/ var prettyPrintOne; /** find all the < pre > and < code > tags in the DOM with class=prettyprint *andprettifythem. *@param{Function}opt_whenDoneifspecified,calledwhenthelastentry *hasbeenfinished.
*/ var prettyPrint;
/** browser detection. @extern */ function _pr_isIE6() { var isIE6 = navigator && navigator.userAgent &&
/\bMSIE 6\./.test(navigator.userAgent);
_pr_isIE6 = function () { return isIE6; }; return isIE6;
}
(function () { /** Splits input on space and returns an Object mapping each non-empty part to *true.
*/ function wordSet(words) {
words = words.split(/ /g); var set = {}; for (var i = words.length; --i >= 0;) { var w = words[i]; if (w) { set[w] = null; }
} return set;
}
// Keyword lists for various languages. 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 "; var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " + "new operator private protected public this throw true try "; 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 typeof using virtual wchar_t where "; var JAVA_KEYWORDS = COMMON_KEYWORDS + "boolean byte extends final finally implements import instanceof null " + "native package strictfp super synchronized throws transient "; var CSHARP_KEYWORDS = JAVA_KEYWORDS + "as base by checked decimal delegate descending event " + "fixed foreach from group implicit in interface internal into is lock " + "object out override orderby params readonly ref sbyte sealed " + "stackalloc string select uint ulong unchecked unsafe ushort var "; var JSCRIPT_KEYWORDS = COMMON_KEYWORDS + "debugger eval export function get null set undefined var with " + "Infinity NaN "; var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " + "goto if import last local my next no our print package redo require " + "sub undef unless until use wantarray while BEGIN END "; var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " + "elif except exec finally from global import in is lambda " + "nonlocal not or pass print raise try with yield " + "False True None "; var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class 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 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 +
PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
// token style names. correspond to css classes /** token style for a string literal */ var PR_STRING = 'str'; /** token style for a keyword */ var PR_KEYWORD = 'kwd'; /** token style for a comment */ var PR_COMMENT = 'com'; /** token style for a type */ var PR_TYPE = 'typ'; /** token style for a literal value. e.g. 1, null, true. */ var PR_LITERAL = 'lit'; /** token style for a punctuation string. */ var PR_PUNCTUATION = 'pun'; /** token style for a punctuation string. */ var PR_PLAIN = 'pln';
/** token style for an sgml tag. */ var PR_TAG = 'tag'; /** token style for a markup declaration such as a DOCTYPE. */ var PR_DECLARATION = 'dec'; /** token style for embedded source. */ var PR_SOURCE = 'src'; /** token style for an sgml attribute name. */ var PR_ATTRIB_NAME = 'atn'; /** token style for an sgml attribute value. */ var PR_ATTRIB_VALUE = 'atv';
/** *Aclassthatindicatesasectionofmarkupthatisnotcode,e.g.toallow *embeddingoflinenumberswithincodelistings.
*/ var PR_NOCODE = 'nocode';
/** A set of tokens that can precede a regular expression literal in *javascript. *http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full *list,butI'veremovedonesthatmightbeproblematicwhenseenin *languagesthatdon'tsupportregularexpressionliterals. * *<p>Specifically,I'veremovedanykeywordsthatcan'tprecedearegexp *literalinasyntacticallylegaljavascriptprogram,andI'veremovedthe *"in"keywordsinceit'snotakeywordinmanylanguages,andmightbeused *asacountofinches. *@private
*/ var REGEXP_PRECEDER_PATTERN = function () { var preceders = [ "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=", "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=", "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";", "<", "<<", "<<=", "<=", "=", "==", "===", ">", ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[", "^", "^=", "^^", "^^=", "{", "|", "|=", "||", "||=", "~"/* handles =~ and !~ */, "break", "case", "continue", "delete", "do", "else", "finally", "instanceof", "return", "throw", "try", "typeof"
]; var pattern = '(?:' + '(?:(?:^|[^0-9.])\\.{1,3})|' + // a dot that's not part of a number '(?:(?:^|[^\\+])\\+)|' + // allow + but not ++ '(?:(?:^|[^\\-])-)'; // allow - but not -- for (var i = 0; i < preceders.length; ++i) { var preceder = preceders[i]; if (isWordChar(preceder.charAt(0))) {
pattern += '|\\b' + preceder;
} else {
pattern += '|' + preceder.replace(/([^=<>:&])/g, '\\$1');
}
}
pattern += '|^)\\s*$'; // matches at end, and matches empty string returnnew RegExp(pattern); // 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.
}();
// Define regexps here so that the interpreter doesn't have to create an // object each time the function containing them is called. // The language spec requires a new object created even if you don't access // the $1 members. var pr_amp = /&/g; var pr_lt = /</g; var pr_gt = />/g; var pr_quot = /\"/g; /** like textToHtml but escapes double quotes to be attribute safe. */ function attribToHtml(str) { return str.replace(pr_amp, '&')
.replace(pr_lt, '<')
.replace(pr_gt, '>')
.replace(pr_quot, '"');
}
/** escapest html special characters to html. */ function textToHtml(str) { return str.replace(pr_amp, '&')
.replace(pr_lt, '<')
.replace(pr_gt, '>');
}
var pr_ltEnt = /</g; var pr_gtEnt = />/g; var pr_aposEnt = /'/g; var pr_quotEnt = /"/g; var pr_ampEnt = /&/g; var pr_nbspEnt = / /g; /** unescapes html to plain text. */ function htmlToText(html) { var pos = html.indexOf('&'); if (pos < 0) { return html; } // Handle numeric entities specially. We can't use functional substitution // since that doesn't work in older versions of Safari. // These should be rare since most browsers convert them to normal chars. for (--pos; (pos = html.indexOf('', pos + 1)) >= 0;) { var end = html.indexOf(';', pos); if (end >= 0) { var num = html.substring(pos + 3, end); var radix = 10; if (num && num.charAt(0) === 'x') {
num = num.substring(1);
radix = 16;
} var codePoint = parseInt(num, radix); if (!isNaN(codePoint)) {
html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
html.substring(end + 1));
}
}
}
/** is the given node's innerHTML normally unescaped? */ function isRawContent(node) { return'XMP' === node.tagName;
}
function normalizedHtml(node, out) { switch (node.nodeType) { case1: // an element var name = node.tagName.toLowerCase();
out.push('<', name); for (var i = 0; i < node.attributes.length; ++i) { var attr = node.attributes[i]; if (!attr.specified) { continue; }
out.push(' ');
normalizedHtml(attr, out);
}
out.push('>'); for (var child = node.firstChild; child; child = child.nextSibling) {
normalizedHtml(child, out);
} if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
out.push('<\/', name, '>');
} break; case2: // an attribute
out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"'); break; case3: case4: // text
out.push(textToHtml(node.nodeValue)); break;
}
}
var PR_innerHtmlWorks = null; function getInnerHtml(node) { // inner html is hopelessly broken in Safari 2.0.4 when the content is // an html description of well formed XML and the containing tag is a PRE // tag, so we detect that case and emulate innerHTML. if (null === PR_innerHtmlWorks) { var testNode = document.createElement('PRE');
testNode.appendChild(
document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
}
if (PR_innerHtmlWorks) { var content = node.innerHTML; // XMP tags contain unescaped entities so require special handling. if (isRawContent(node)) {
content = textToHtml(content);
} return content;
}
var out = []; for (var child = node.firstChild; child; child = child.nextSibling) {
normalizedHtml(child, out);
} return out.join('');
}
/** returns a function that expand tabs to spaces. This function can be fed *successivechunksoftext,andwillmaintainitsowninternalstateto *keeptrackofhowtabsareexpanded. *@return{function(string):string}afunctionthattakes *plaintextandreturnthetextwithtabsexpanded. *@private
*/ function makeTabExpander(tabWidth) { var SPACES = ' '; var charInLine = 0;
returnfunction (plainText) { // walk over each character looking for tabs and newlines. // On tabs, expand them. On newlines, reset charInLine. // Otherwise increment charInLine var out = null; var pos = 0; for (var i = 0, n = plainText.length; i < n; ++i) { var ch = plainText.charAt(i);
switch (ch) { case'\t': if (!out) { out = []; }
out.push(plainText.substring(pos, i)); // calculate how much space we need in front of this part // nSpaces is the amount of padding -- the number of spaces needed // to move us to the next column, where columns occur at factors of // tabWidth. var nSpaces = tabWidth - (charInLine % tabWidth);
charInLine += nSpaces; for (; nSpaces >= 0; nSpaces -= SPACES.length) {
out.push(SPACES.substring(0, nSpaces));
}
pos = i + 1; break; case'\n':
charInLine = 0; break; default:
++charInLine;
}
} if (!out) { return plainText; }
out.push(plainText.substring(pos)); return out.join('');
};
}
// The below pattern matches one of the following // (1) /[^<]+/ : A run of characters other than '<' // (2) /<!--.*?-->/: an HTML comment // (3) /<!\[CDATA\[.*?\]\]>/: a cdata section // (3) /<\/?[a-zA-Z][^>]*>/ : A probably tag that should not be highlighted // (4) /</ : A '<' that does not begin a larger chunk. Treated as 1 var pr_chunkPattern =
/(?:[^<]+|<!--[\s\S]*?-->|<!\[CDATA\[([\s\S]*?)\]\]>|<\/?[a-zA-Z][^>]*>|<)/g; var pr_commentPrefix = /^<!--/; var pr_cdataPrefix = /^<\[CDATA\[/; var pr_brPrefix = /^<br\b/i; var pr_tagNameRe = /^<(\/?)([a-zA-Z]+)/;
/** split markup into chunks of html tags (style null) and *plaintext(style{@link#PR_PLAIN}),convertingtagswhichare *significantfortokenization(<br>)intotheirtextualequivalent. * *@param{string}shtmlwherewhitespaceisconsideredsignificant. *@return{Object}sourcecodeandextractedtags. *@private
*/ function extractTags(s) { // since the pattern has the 'g' modifier and defines no capturing groups, // this will return a list of all chunks which we then classify and wrap as // PR_Tokens var matches = s.match(pr_chunkPattern); var sourceBuf = []; var sourceBufLen = 0; var extractedTags = []; if (matches) { for (var i = 0, n = matches.length; i < n; ++i) { var match = matches[i]; if (match.length > 1 && match.charAt(0) === '<') { if (pr_commentPrefix.test(match)) { continue; } if (pr_cdataPrefix.test(match)) { // strip CDATA prefix and suffix. Don't unescape since it's CDATA
sourceBuf.push(match.substring(9, match.length - 3));
sourceBufLen += match.length - 12;
} elseif (pr_brPrefix.test(match)) { // <br> tags are lexically significant so convert them to text. // This is undone later.
sourceBuf.push('\n');
++sourceBufLen;
} else { if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) { // A <span class="nocode"> will start a section that should be // ignored. Continue walking the list until we see a matching end // tag. var name = match.match(pr_tagNameRe)[2]; var depth = 1;
end_tag_loop: for (var j = i + 1; j < n; ++j) { var name2 = matches[j].match(pr_tagNameRe); if (name2 && name2[2] === name) { if (name2[1] === '/') { if (--depth === 0) { break end_tag_loop; }
} else {
++depth;
}
}
} if (j < n) {
extractedTags.push(
sourceBufLen, matches.slice(i, j + 1).join(''));
i = j;
} else { // Ignore unclosed sections.
extractedTags.push(sourceBufLen, match);
}
} else {
extractedTags.push(sourceBufLen, match);
}
}
} else { var literalText = htmlToText(match);
sourceBuf.push(literalText);
sourceBufLen += literalText.length;
}
}
} return { source: sourceBuf.join(''), tags: extractedTags };
}
/** True if the given tag contains a class attribute with the nocode class. */ function isNoCodeTag(tag) { return !!tag // First canonicalize the representation of attributes
.replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g, ' $1="$2$3$4"') // Then look for the attribute we want.
.match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
}
/** Given triples of [style, pattern, context] returns a lexing function, *Thelexingfunctioninterpretsthepatternstofindtokenboundariesand *returnsadecorationlistoftheform *[index_0,style_0,index_1,style_1,...,index_n,style_n] *whereindex_nisanindexintothesourceCode,andstyle_nisastyle *constantlikePR_PLAIN.index_n-1<=index_n,andstyle_n-1appliesto *allcharactersinsourceCode[index_n-1:index_n]. * *ThestylePatternsisalistwhoseelementshavetheform *[style:string,pattern:RegExp,context:RegExp,shortcut:string]. & *StyleisastyleconstantlikePR_PLAIN. * *Patternmustonlymatchprefixes,andifitmatchesaprefixandcontext *isnullormatchesthelastnon-commenttokenparsed,thenthatmatchis *consideredatokenwiththesamestyle. * *Contextisappliedtothelastnon-whitespace,non-commenttoken *recognized. * *Shortcutisanoptionalstringofcharacters,anyofwhich,ifthefirst *character,guranteethatthispatternandonlythispatternmatches. * *@param{Array}shortcutStylePatternspatternsthatalwaysstartwith *aknowncharacter.Musthaveashortcutstring. *@param{Array}fallthroughStylePatternspatternsthatwillbetriedin *orderiftheshortcutonesfail.Mayhaveshortcuts. * *@return{function(string,number?):Array.<number|string>}a *functionthattakessourcecodeandreturnsalistofdecorations.
*/ function createSimpleLexer(shortcutStylePatterns,
fallthroughStylePatterns) { var shortcuts = {};
(function () { var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns); for (var i = allPatterns.length; --i >= 0;) { var patternParts = allPatterns[i]; var shortcutChars = patternParts[3]; if (shortcutChars) { for (var c = shortcutChars.length; --c >= 0;) {
shortcuts[shortcutChars.charAt(c)] = patternParts;
}
}
}
})();
var nPatterns = fallthroughStylePatterns.length; var notWs = /\S/;
returnfunction (sourceCode, opt_basePos) {
opt_basePos = opt_basePos || 0; var decorations = [opt_basePos, PR_PLAIN]; var lastToken = ''; var pos = 0; // index into sourceCode var tail = sourceCode;
while (tail.length) { var style; var token = null; var match;
var patternParts = shortcuts[tail.charAt(0)]; if (patternParts) {
match = tail.match(patternParts[1]);
token = match[0];
style = patternParts[0];
} else { for (var i = 0; i < nPatterns; ++i) {
patternParts = fallthroughStylePatterns[i]; var contextPattern = patternParts[2]; if (contextPattern && !contextPattern.test(lastToken)) { // rule can't be used continue;
}
match = tail.match(patternParts[1]); if (match) {
token = match[0];
style = patternParts[0]; break;
}
}
if (!token) { // make sure that we make progress
style = PR_PLAIN;
token = tail.substring(0, 1);
}
}
var PR_MARKUP_LEXER = createSimpleLexer([], [
[PR_PLAIN, /^[^<]+/, null],
[PR_DECLARATION, /^<!\w[^>]*(?:>|$)/, null],
[PR_COMMENT, /^<!--[\s\S]*?(?:-->|$)/, null],
[PR_SOURCE, /^<\?[\s\S]*?(?:\?>|$)/, null],
[PR_SOURCE, /^<%[\s\S]*?(?:%>|$)/, null],
[PR_SOURCE, // Tags whose content is not escaped, and which contain source code.
/^<(script|style|xmp)\b[^>]*>[\s\S]*?<\/\1\b[^>]*>/i, null],
[PR_TAG, /^<\/?\w[^<>]*>/, null]
]); // Splits any of the source|style|xmp entries above into a start tag, // source content, and end tag. var PR_SOURCE_CHUNK_PARTS = /^(<[^>]*>)([\s\S]*)(<\/[^>]*>)$/; /** split markup on tags, comments, application directives, and other top *levelconstructs.Tagsarereturnedasasingletoken-attributesare *notyetbrokenout. *@private
*/ function tokenizeMarkup(source) { var decorations = PR_MARKUP_LEXER(source); for (var i = 0; i < decorations.length; i += 2) { if (decorations[i + 1] === PR_SOURCE) { var start, end;
start = decorations[i];
end = i + 2 < decorations.length ? decorations[i + 2] : source.length; // Split out start and end script tags as actual tags, and leave the // body with style SCRIPT. var sourceChunk = source.substring(start, end); var match = sourceChunk.match(PR_SOURCE_CHUNK_PARTS); if (match) {
decorations.splice(
i, 2,
start, PR_TAG, // the open chunk
start + match[1].length, PR_SOURCE,
start + match[1].length + (match[2] || '').length, PR_TAG);
}
}
} return decorations;
}
var PR_TAG_LEXER = createSimpleLexer([
[PR_ATTRIB_VALUE, /^\'[^\']*(?:\'|$)/, null, "'"],
[PR_ATTRIB_VALUE, /^\"[^\"]*(?:\"|$)/, null, '"'],
[PR_PUNCTUATION, /^[<>\/=]+/, null, '<>/=']
], [
[PR_TAG, /^[\w:\-]+/, /^</],
[PR_ATTRIB_VALUE, /^[\w\-]+/, /^=/],
[PR_ATTRIB_NAME, /^[\w:\-]+/, null],
[PR_PLAIN, /^\s+/, null, ' \t\r\n']
]); /** split tags attributes and their values out from the tag name, and *recursivelylexsourcechunks. *@private
*/ function splitTagAttributes(source, decorations) { for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; if (style === PR_TAG) { var start, end;
start = decorations[i];
end = i + 2 < decorations.length ? decorations[i + 2] : source.length; var chunk = source.substring(start, end); var subDecorations = PR_TAG_LEXER(chunk, start);
spliceArrayInto(subDecorations, decorations, i, 2);
i += subDecorations.length - 2;
}
} return decorations;
}
/** returns a function that produces a list of decorations from source text. * *Thiscodetreats",',and`asstringdelimiters,and\asastring *escape.Itdoesnotrecognizeperl'sqq()stylestrings. *Ithasnospecialhandlingfordoubledelimiterescapesasinbasic,or *thetripleddelimitersusedinpython,butshouldworkonthoseregardless *althoughinthosecasesasinglestringliteralmaybebrokenupinto *multipleadjacentstringliterals. * *ItrecognizesC,C++,andshellstylecomments. * *@param{Object}optionsasetofoptionalparameters. *@return{function(string):Array.<string|number>}a *decoratorthattakessourceCodeasplaintextandthatreturnsa *decorationlist
*/ function sourceDecorator(options) { var shortcutStylePatterns = [], fallthroughStylePatterns = []; if (options.tripleQuotedStrings) { // '''multi-line-string''', 'single-line-string', and double-quoted
shortcutStylePatterns.push(
[PR_STRING, /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/, null, '\'"']);
} elseif (options.multiLineStrings) { // 'multi-line-string', "multi-line-string"
shortcutStylePatterns.push(
[PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/, null, '\'"`']);
} else { // 'single-line-string', "single-line-string"
shortcutStylePatterns.push(
[PR_STRING,
/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/, null, '"\'']);
}
fallthroughStylePatterns.push(
[PR_PLAIN, /^(?:[^\'\"\`\/\#]+)/, null, ' \r\n']); if (options.hashComments) {
shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
} if (options.cStyleComments) {
fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
fallthroughStylePatterns.push(
[PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
} if (options.regexLiterals) { var REGEX_LITERAL = ( // A regular expression literal starts with a slash that is // not followed by * or / so that it is not confused with // comments. '^/(?=[^/*])' // and then contains any number of raw characters,
+ '(?:[^/\\x5B\\x5C]' // escape sequences (\x5C),
+ '|\\x5C[\\s\\S]' // or non-nesting character sets (\x5B\x5D);
+ '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+' // finally closed by a /.
+ '(?:/|$)');
fallthroughStylePatterns.push(
[PR_STRING, new RegExp(REGEX_LITERAL), REGEXP_PRECEDER_PATTERN]);
}
var keywords = wordSet(options.keywords);
options = null;
/** splits the given string into comment, string, and "other" tokens. *@param{string}sourceCodeasplaintext *@return{Array.<number|string>}adecorationlist. *@private
*/ var splitStringAndCommentTokens = createSimpleLexer(
shortcutStylePatterns, fallthroughStylePatterns);
var styleLiteralIdentifierPuncRecognizer = createSimpleLexer([], [
[PR_PLAIN, /^\s+/, null, ' \r\n'], // TODO(mikesamuel): recognize non-latin letters and numerals in idents
[PR_PLAIN, /^[a-z_$@][a-z_$@0-9]*/i, null], // A hex number
[PR_LITERAL, /^0x[a-f0-9]+[a-z]/i, null], // An octal or decimal number, possibly in scientific notation
[PR_LITERAL,
/^(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d+)(?:e[+\-]?\d+)?[a-z]*/i, null, '123456789'],
[PR_PUNCTUATION, /^[^\s\w\.$@]+/, null] // Fallback will handle decimal points not adjacent to a digit
]);
/** splits plain text tokens into more specific tokens, and then tries to *recognizekeywords,andtypes. *@private
*/ function splitNonStringNonCommentTokens(source, decorations) { for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; if (style === PR_PLAIN) { var start, end, chunk, subDecs;
start = decorations[i];
end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
chunk = source.substring(start, end);
subDecs = styleLiteralIdentifierPuncRecognizer(chunk, start); for (var j = 0, m = subDecs.length; j < m; j += 2) { var subStyle = subDecs[j + 1]; if (subStyle === PR_PLAIN) { var subStart = subDecs[j]; var subEnd = j + 2 < m ? subDecs[j + 2] : chunk.length; var token = source.substring(subStart, subEnd); if (token === '.') {
subDecs[j + 1] = PR_PUNCTUATION;
} elseif (token in keywords) {
subDecs[j + 1] = PR_KEYWORD;
} elseif (/^@?[A-Z][A-Z$]*[a-z][A-Za-z$]*$/.test(token)) { // classify types and annotations using Java's style conventions
subDecs[j + 1] = token.charAt(0) === '@' ? PR_LITERAL : PR_TYPE;
}
}
}
spliceArrayInto(subDecs, decorations, i, 2);
i += subDecs.length - 2;
}
} return decorations;
}
returnfunction (sourceCode) { // Split into strings, comments, and other. // We do this because strings and comments are easily recognizable and can // contain stuff that looks like other tokens, so we want to mark those // early so we don't recurse into them. var decorations = splitStringAndCommentTokens(sourceCode);
// Split non comment|string tokens on whitespace and word boundaries
decorations = splitNonStringNonCommentTokens(sourceCode, decorations);
/** identify regions of markup that are really source code, and recursivley *lexthem. *@private
*/ function splitSourceNodes(source, decorations) { for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; if (style === PR_SOURCE) { // Recurse using the non-markup lexer var start, end;
start = decorations[i];
end = i + 2 < decorations.length ? decorations[i + 2] : source.length; var subDecorations = decorateSource(source.substring(start, end)); for (var j = 0, m = subDecorations.length; j < m; j += 2) {
subDecorations[j] += start;
}
spliceArrayInto(subDecorations, decorations, i, 2);
i += subDecorations.length - 2;
}
} return decorations;
}
/** identify attribute values that really contain source code and recursively *lexthem. *@private
*/ function splitSourceAttributes(source, decorations) { var nextValueIsSource = false; for (var i = 0; i < decorations.length; i += 2) { var style = decorations[i + 1]; var start, end; if (style === PR_ATTRIB_NAME) {
start = decorations[i];
end = i + 2 < decorations.length ? decorations[i + 2] : source.length;
nextValueIsSource = /^on|^style$/i.test(source.substring(start, end));
} elseif (style === PR_ATTRIB_VALUE) { if (nextValueIsSource) {
start = decorations[i];
end = i + 2 < decorations.length ? decorations[i + 2] : source.length; var attribValue = source.substring(start, end); var attribLen = attribValue.length; var quoted =
(attribLen >= 2 && /^[\"\']/.test(attribValue) &&
attribValue.charAt(0) === attribValue.charAt(attribLen - 1));
var attribSource; var attribSourceStart; var attribSourceEnd; if (quoted) {
attribSourceStart = start + 1;
attribSourceEnd = end - 1;
attribSource = attribValue;
} else {
attribSourceStart = start + 1;
attribSourceEnd = end - 1;
attribSource = attribValue.substring(1, attribValue.length - 1);
}
var attribSourceDecorations = decorateSource(attribSource); for (var j = 0, m = attribSourceDecorations.length; j < m; j += 2) {
attribSourceDecorations[j] += attribSourceStart;
}
if (quoted) {
attribSourceDecorations.push(attribSourceEnd, PR_ATTRIB_VALUE);
spliceArrayInto(attribSourceDecorations, decorations, i + 2, 0);
} else {
spliceArrayInto(attribSourceDecorations, decorations, i, 2);
}
}
nextValueIsSource = false;
}
} return decorations;
}
/** returns a decoration list given a string of markup. * *Thiscoderecognizesanumberofconstructs. *<!--...-->comment *<!\w...>declaration *<\w...>tag *</\w...>tag *<?...?>embeddedsource *<%...%>embeddedsource *&[#\w]...;entity * *Itdoesnotrecognizes%foo;doctypeentitiesfrom. * *Itwillrecurseintoany<style>,<script>,andon*attributesusing *PR_lexSource.
*/ function decorateMarkup(sourceCode) { // This function works as follows: // 1) Start by splitting the markup into text and tag chunks // Input: string s // Output: List<PR_Token> where style in (PR_PLAIN, null) // 2) Then split the text chunks further into comments, declarations, // tags, etc. // After each split, consider whether the token is the start of an // embedded source section, i.e. is an open <script> tag. If it is, find // the corresponding close token, and don't bother to lex in between. // Input: List<string> // Output: List<PR_Token> with style in // (PR_TAG, PR_PLAIN, PR_SOURCE, null) // 3) Finally go over each tag token and split out attribute names and // values. // Input: List<PR_Token> // Output: List<PR_Token> where style in // (PR_TAG, PR_PLAIN, PR_SOURCE, NAME, VALUE, null) var decorations = tokenizeMarkup(sourceCode);
decorations = splitTagAttributes(sourceCode, decorations);
decorations = splitSourceNodes(sourceCode, decorations);
decorations = splitSourceAttributes(sourceCode, decorations); return decorations;
}
/** *@param{string}sourceTextplaintext *@param{Array.<number|string>}extractedTagschunksofrawhtmlpreceded *bytheirpositioninsourceTextinorder. *@param{Array.<number|string>}decorationsstyleclassesprecededbytheir *positioninsourceTextinorder. *@return{string}html *@private
*/ function recombineTagsAndDecorations(sourceText, extractedTags, decorations) { var html = []; // index past the last char in sourceText written to html var outputIdx = 0;
var openDecoration = null; var currentDecoration = null; var tagPos = 0; // index into extractedTags var decPos = 0; // index into decorations var tabExpander = makeTabExpander(PR_TAB_WIDTH);
var adjacentSpaceRe = /([\r\n ]) /g; var startOrSpaceRe = /(^| ) /gm; var newlineRe = /\r\n?|\n/g; var trailingSpaceRe = /[ \r\n]$/; var lastWasSpace = true; // the last text chunk emitted ended with a space.
// A helper function that is responsible for opening sections of decoration // and outputing properly escaped chunks of source function emitTextUpTo(sourceIdx) { if (sourceIdx > outputIdx) { if (openDecoration && openDecoration !== currentDecoration) { // Close the current decoration
html.push('</span>');
openDecoration = null;
} if (!openDecoration && currentDecoration) {
openDecoration = currentDecoration;
html.push('<span class="', openDecoration, '">');
} // This interacts badly with some wikis which introduces paragraph tags // into pre blocks for some strange reason. // It's necessary for IE though which seems to lose the preformattedness // of <pre> tags when their innerHTML is assigned. // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html // and it serves to undo the conversion of <br>s to newlines done in // chunkify. var htmlChunk = textToHtml(
tabExpander(sourceText.substring(outputIdx, sourceIdx)))
.replace(lastWasSpace
? startOrSpaceRe
: adjacentSpaceRe, '$1 '); // Keep track of whether we need to escape space at the beginning of the // next chunk.
lastWasSpace = trailingSpaceRe.test(htmlChunk);
html.push(htmlChunk.replace(newlineRe, '<br />'));
outputIdx = sourceIdx;
}
}
while (true) { // Determine if we're going to consume a tag this time around. Otherwise // we consume a decoration or exit. var outputTag; if (tagPos < extractedTags.length) { if (decPos < decorations.length) { // Pick one giving preference to extractedTags since we shouldn't open // a new style that we're going to have to immediately close in order // to output a tag.
outputTag = extractedTags[tagPos] <= decorations[decPos];
} else {
outputTag = true;
}
} else {
outputTag = false;
} // Consume either a decoration or a tag or exit. if (outputTag) {
emitTextUpTo(extractedTags[tagPos]); if (openDecoration) { // Close the current decoration
html.push('</span>');
openDecoration = null;
}
html.push(extractedTags[tagPos + 1]);
tagPos += 2;
} elseif (decPos < decorations.length) {
emitTextUpTo(decorations[decPos]);
currentDecoration = decorations[decPos + 1];
decPos += 2;
} else { break;
}
}
emitTextUpTo(sourceText.length); if (openDecoration) {
html.push('</span>');
}
function prettyPrintOne(sourceCodeHtml, opt_langExtension) { try { // Extract tags, and convert the source code to plain text. var sourceAndExtractedTags = extractTags(sourceCodeHtml); /** Plain text. @type {string} */ var source = sourceAndExtractedTags.source;
/** Even entries are positions in source in ascending order. Odd entries *aretagsthatwereextractedatthatposition. *@type{Array.<number|string>}
*/ var extractedTags = sourceAndExtractedTags.tags;
// Pick a lexer and apply it. if (!langHandlerRegistry.hasOwnProperty(opt_langExtension)) { // Treat it as markup if the first non whitespace character is a < and // the last non-whitespace character is a >.
opt_langExtension =
/^\s*</.test(source) ? 'default-markup' : 'default-code';
}
/** Even entries are positions in source in ascending order. Odd enties *arestylemarkers(e.g.,PR_COMMENT)thatrunfromthatpositionuntil *theend. *@type{Array.<number|string>}
*/ var decorations = langHandlerRegistry[opt_langExtension].call({}, source);
// Integrate the decorations and tags back into the source code to produce // a decorated html string. return recombineTagsAndDecorations(source, extractedTags, decorations);
} catch (e) { if ('console' in window) {
console.log(e);
console.trace();
} return sourceCodeHtml;
}
}
function prettyPrint(opt_whenDone) { var isIE6 = _pr_isIE6();
// fetch a list of nodes to rewrite var codeSegments = [
document.getElementsByTagName('pre'),
document.getElementsByTagName('code'),
document.getElementsByTagName('xmp') ]; var elements = []; for (var i = 0; i < codeSegments.length; ++i) { for (var j = 0; j < codeSegments[i].length; ++j) {
elements.push(codeSegments[i][j]);
}
}
codeSegments = null;
// the loop is broken into a series of continuations to make sure that we // don't make the browser unresponsive when rewriting a large page. var k = 0;
function doWork() { var endTime = (PR_SHOULD_USE_CONTINUATION ? new Date().getTime() + 250/* ms */ :
Infinity); for (; k < elements.length && new Date().getTime() < endTime; k++) { var cs = elements[k]; if (cs.className && cs.className.indexOf('prettyprint') >= 0) { // If the classes includes a language extensions, use it. // Language extensions can be specified like // <pre class="prettyprint lang-cpp"> // the language extension "cpp" is used to find a language handler as // passed to PR_registerLangHandler. var langExtension = cs.className.match(/\blang-(\w+)\b/); if (langExtension) { langExtension = langExtension[1]; }
// 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) {
nested = true; break;
}
} if (!nested) { // fetch the content as a snippet of properly escaped HTML. // Firefox adds newlines at the end. var content = getInnerHtml(cs);
content = content.replace(/(?:\r\n?|\n)$/, '');
// do the pretty printing var newContent = prettyPrintOne(content, langExtension);
// push the prettified html back into the tag. if (!isRawContent(cs)) { // just replace the old html with the new
cs.innerHTML = newContent;
} else { // we need to change the tag to a <pre> since <xmp>s do not allow // embedded tags such as the span tags used to attach styles to // sections of source code. var pre = document.createElement('PRE'); for (var i = 0; i < cs.attributes.length; ++i) { var a = cs.attributes[i]; if (a.specified) { var aname = a.name.toLowerCase(); if (aname === 'class') {
pre.className = a.value; // For IE 6
} else {
pre.setAttribute(a.name, a.value);
}
}
}
pre.innerHTML = newContent;
// remove the old
cs.parentNode.replaceChild(pre, cs);
cs = pre;
}
// Replace <br>s with line-feeds so that copying and pasting works // on IE 6. // Doing this on other browsers breaks lots of stuff since \r\n is // treated as two newlines on Firefox, and doing this also slows // down rendering. if (isIE6 && cs.tagName === 'PRE') { var lineBreaks = cs.getElementsByTagName('br'); for (var j = lineBreaks.length; --j >= 0;) { var lineBreak = lineBreaks[j];
lineBreak.parentNode.replaceChild(
document.createTextNode('\r\n'), lineBreak);
}
}
}
}
} if (k < elements.length) { // finish up in a continuation
setTimeout(doWork, 250);
} elseif (opt_whenDone) {
opt_whenDone();
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.