Biblioteca Java - Blame information for rev 32
Subversion Repositories:
(root)/Courses and labs samples/ISP/Exemple_ISP_Cluj_2015/ElevatorCurs/.jacocoverage/report.html/.resources/prettify.js
Rev | Author | Line No. | Line |
---|---|---|---|
32 | mihai | 1 | // Copyright (C) 2006 Google Inc. |
2 | // | ||
3 | // Licensed under the Apache License, Version 2.0 (the "License"); | ||
4 | // you may not use this file except in compliance with the License. | ||
5 | // You may obtain a copy of the License at | ||
6 | // | ||
7 | // http://www.apache.org/licenses/LICENSE-2.0 | ||
8 | // | ||
9 | // Unless required by applicable law or agreed to in writing, software | ||
10 | // distributed under the License is distributed on an "AS IS" BASIS, | ||
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
12 | // See the License for the specific language governing permissions and | ||
13 | // limitations under the License. | ||
14 | |||
15 | |||
16 | /** | ||
17 | * @fileoverview | ||
18 | * some functions for browser-side pretty printing of code contained in html. | ||
19 | * <p> | ||
20 | * | ||
21 | * For a fairly comprehensive set of languages see the | ||
22 | * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a> | ||
23 | * file that came with this source. At a minimum, the lexer should work on a | ||
24 | * number of languages including C and friends, Java, Python, Bash, SQL, HTML, | ||
25 | * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk | ||
26 | * and a subset of Perl, but, because of commenting conventions, doesn't work on | ||
27 | * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. | ||
28 | * <p> | ||
29 | * Usage: <ol> | ||
30 | * <li> include this source file in an html page via | ||
31 | * {@code <script type="text/javascript" src="/path/to/prettify.js"></script>} | ||
32 | * <li> define style rules. See the example page for examples. | ||
33 | * <li> mark the {@code <pre>} and {@code <code>} tags in your source with | ||
34 | * {@code class=prettyprint.} | ||
35 | * You can also use the (html deprecated) {@code <xmp>} tag, but the pretty | ||
36 | * printer needs to do more substantial DOM manipulations to support that, so | ||
37 | * some css styles may not be preserved. | ||
38 | * </ol> | ||
39 | * That's it. I wanted to keep the API as simple as possible, so there's no | ||
40 | * need to specify which language the code is in, but if you wish, you can add | ||
41 | * another class to the {@code <pre>} or {@code <code>} element to specify the | ||
42 | * language, as in {@code <pre class="prettyprint lang-java">}. Any class that | ||
43 | * starts with "lang-" followed by a file extension, specifies the file type. | ||
44 | * See the "lang-*.js" files in this directory for code that implements | ||
45 | * per-language file handlers. | ||
46 | * <p> | ||
47 | * Change log:<br> | ||
48 | * cbeust, 2006/08/22 | ||
49 | * <blockquote> | ||
50 | * Java annotations (start with "@") are now captured as literals ("lit") | ||
51 | * </blockquote> | ||
52 | * @requires console | ||
53 | */ | ||
54 | |||
55 | // JSLint declarations | ||
56 | /*global console, document, navigator, setTimeout, window */ | ||
57 | |||
58 | /** | ||
59 | * Split {@code prettyPrint} into multiple timeouts so as not to interfere with | ||
60 | * UI events. | ||
61 | * If set to {@code false}, {@code prettyPrint()} is synchronous. | ||
62 | */ | ||
63 | window['PR_SHOULD_USE_CONTINUATION'] = true; | ||
64 | |||
65 | /** the number of characters between tab columns */ | ||
66 | window['PR_TAB_WIDTH'] = 8; | ||
67 | |||
68 | /** Walks the DOM returning a properly escaped version of innerHTML. | ||
69 | * @param {Node} node | ||
70 | * @param {Array.<string>} out output buffer that receives chunks of HTML. | ||
71 | */ | ||
72 | window['PR_normalizedHtml'] | ||
73 | |||
74 | /** Contains functions for creating and registering new language handlers. | ||
75 | * @type {Object} | ||
76 | */ | ||
77 | = window['PR'] | ||
78 | |||
79 | /** Pretty print a chunk of code. | ||
80 | * | ||
81 | * @param {string} sourceCodeHtml code as html | ||
82 | * @return {string} code as html, but prettier | ||
83 | */ | ||
84 | = window['prettyPrintOne'] | ||
85 | /** Find all the {@code <pre>} and {@code <code>} tags in the DOM with | ||
86 | * {@code class=prettyprint} and prettify them. | ||
87 | * @param {Function?} opt_whenDone if specified, called when the last entry | ||
88 | * has been finished. | ||
89 | */ | ||
90 | = window['prettyPrint'] = void 0; | ||
91 | |||
92 | /** browser detection. @extern @returns false if not IE, otherwise the major version. */ | ||
93 | window['_pr_isIE6'] = function () { | ||
94 | var ieVersion = navigator && navigator.userAgent && | ||
95 | navigator.userAgent.match(/\bMSIE ([678])\./); | ||
96 | ieVersion = ieVersion ? +ieVersion[1] : false; | ||
97 | window['_pr_isIE6'] = function () { return ieVersion; }; | ||
98 | return ieVersion; | ||
99 | }; | ||
100 | |||
101 | |||
102 | (function () { | ||
103 | // Keyword lists for various languages. | ||
104 | var FLOW_CONTROL_KEYWORDS = | ||
105 | "break continue do else for if return while "; | ||
106 | var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " + | ||
107 | "double enum extern float goto int long register short signed sizeof " + | ||
108 | "static struct switch typedef union unsigned void volatile "; | ||
109 | var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " + | ||
110 | "new operator private protected public this throw true try typeof "; | ||
111 | var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " + | ||
112 | "concept concept_map const_cast constexpr decltype " + | ||
113 | "dynamic_cast explicit export friend inline late_check " + | ||
114 | "mutable namespace nullptr reinterpret_cast static_assert static_cast " + | ||
115 | "template typeid typename using virtual wchar_t where "; | ||
116 | var JAVA_KEYWORDS = COMMON_KEYWORDS + | ||
117 | "abstract boolean byte extends final finally implements import " + | ||
118 | "instanceof null native package strictfp super synchronized throws " + | ||
119 | "transient "; | ||
120 | var CSHARP_KEYWORDS = JAVA_KEYWORDS + | ||
121 | "as base by checked decimal delegate descending event " + | ||
122 | "fixed foreach from group implicit in interface internal into is lock " + | ||
123 | "object out override orderby params partial readonly ref sbyte sealed " + | ||
124 | "stackalloc string select uint ulong unchecked unsafe ushort var "; | ||
125 | var JSCRIPT_KEYWORDS = COMMON_KEYWORDS + | ||
126 | "debugger eval export function get null set undefined var with " + | ||
127 | "Infinity NaN "; | ||
128 | var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " + | ||
129 | "goto if import last local my next no our print package redo require " + | ||
130 | "sub undef unless until use wantarray while BEGIN END "; | ||
131 | var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " + | ||
132 | "elif except exec finally from global import in is lambda " + | ||
133 | "nonlocal not or pass print raise try with yield " + | ||
134 | "False True None "; | ||
135 | var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" + | ||
136 | " defined elsif end ensure false in module next nil not or redo rescue " + | ||
137 | "retry self super then true undef unless until when yield BEGIN END "; | ||
138 | var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " + | ||
139 | "function in local set then until "; | ||
140 | var ALL_KEYWORDS = ( | ||
141 | CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS + | ||
142 | PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS); | ||
143 | |||
144 | // token style names. correspond to css classes | ||
145 | /** token style for a string literal */ | ||
146 | var PR_STRING = 'str'; | ||
147 | /** token style for a keyword */ | ||
148 | var PR_KEYWORD = 'kwd'; | ||
149 | /** token style for a comment */ | ||
150 | var PR_COMMENT = 'com'; | ||
151 | /** token style for a type */ | ||
152 | var PR_TYPE = 'typ'; | ||
153 | /** token style for a literal value. e.g. 1, null, true. */ | ||
154 | var PR_LITERAL = 'lit'; | ||
155 | /** token style for a punctuation string. */ | ||
156 | var PR_PUNCTUATION = 'pun'; | ||
157 | /** token style for a punctuation string. */ | ||
158 | var PR_PLAIN = 'pln'; | ||
159 | |||
160 | /** token style for an sgml tag. */ | ||
161 | var PR_TAG = 'tag'; | ||
162 | /** token style for a markup declaration such as a DOCTYPE. */ | ||
163 | var PR_DECLARATION = 'dec'; | ||
164 | /** token style for embedded source. */ | ||
165 | var PR_SOURCE = 'src'; | ||
166 | /** token style for an sgml attribute name. */ | ||
167 | var PR_ATTRIB_NAME = 'atn'; | ||
168 | /** token style for an sgml attribute value. */ | ||
169 | var PR_ATTRIB_VALUE = 'atv'; | ||
170 | |||
171 | /** | ||
172 | * A class that indicates a section of markup that is not code, e.g. to allow | ||
173 | * embedding of line numbers within code listings. | ||
174 | */ | ||
175 | var PR_NOCODE = 'nocode'; | ||
176 | |||
177 | /** A set of tokens that can precede a regular expression literal in | ||
178 | * javascript. | ||
179 | * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full | ||
180 | * list, but I've removed ones that might be problematic when seen in | ||
181 | * languages that don't support regular expression literals. | ||
182 | * | ||
183 | * <p>Specifically, I've removed any keywords that can't precede a regexp | ||
184 | * literal in a syntactically legal javascript program, and I've removed the | ||
185 | * "in" keyword since it's not a keyword in many languages, and might be used | ||
186 | * as a count of inches. | ||
187 | * | ||
188 | * <p>The link a above does not accurately describe EcmaScript rules since | ||
189 | * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works | ||
190 | * very well in practice. | ||
191 | * | ||
192 | * @private | ||
193 | */ | ||
194 | var REGEXP_PRECEDER_PATTERN = function () { | ||
195 | var preceders = [ | ||
196 | "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=", | ||
197 | "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=", | ||
198 | "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";", | ||
199 | "<", "<<", "<<=", "<=", "=", "==", "===", ">", | ||
200 | ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[", | ||
201 | "^", "^=", "^^", "^^=", "{", "|", "|=", "||", | ||
202 | "||=", "~" /* handles =~ and !~ */, | ||
203 | "break", "case", "continue", "delete", | ||
204 | "do", "else", "finally", "instanceof", | ||
205 | "return", "throw", "try", "typeof" | ||
206 | ]; | ||
207 | var pattern = '(?:^^|[+-]'; | ||
208 | for (var i = 0; i < preceders.length; ++i) { | ||
209 | pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1'); | ||
210 | } | ||
211 | pattern += ')\\s*'; // matches at end, and matches empty string | ||
212 | return pattern; | ||
213 | // CAVEAT: this does not properly handle the case where a regular | ||
214 | // expression immediately follows another since a regular expression may | ||
215 | // have flags for case-sensitivity and the like. Having regexp tokens | ||
216 | // adjacent is not valid in any language I'm aware of, so I'm punting. | ||
217 | // TODO: maybe style special characters inside a regexp as punctuation. | ||
218 | }(); | ||
219 | |||
220 | // Define regexps here so that the interpreter doesn't have to create an | ||
221 | // object each time the function containing them is called. | ||
222 | // The language spec requires a new object created even if you don't access | ||
223 | // the $1 members. | ||
224 | var pr_amp = /&/g; | ||
225 | var pr_lt = /</g; | ||
226 | var pr_gt = />/g; | ||
227 | var pr_quot = /\"/g; | ||
228 | /** like textToHtml but escapes double quotes to be attribute safe. */ | ||
229 | function attribToHtml(str) { | ||
230 | return str.replace(pr_amp, '&') | ||
231 | .replace(pr_lt, '<') | ||
232 | .replace(pr_gt, '>') | ||
233 | .replace(pr_quot, '"'); | ||
234 | } | ||
235 | |||
236 | /** escapest html special characters to html. */ | ||
237 | function textToHtml(str) { | ||
238 | return str.replace(pr_amp, '&') | ||
239 | .replace(pr_lt, '<') | ||
240 | .replace(pr_gt, '>'); | ||
241 | } | ||
242 | |||
243 | |||
244 | var pr_ltEnt = /</g; | ||
245 | var pr_gtEnt = />/g; | ||
246 | var pr_aposEnt = /'/g; | ||
247 | var pr_quotEnt = /"/g; | ||
248 | var pr_ampEnt = /&/g; | ||
249 | var pr_nbspEnt = / /g; | ||
250 | /** unescapes html to plain text. */ | ||
251 | function htmlToText(html) { | ||
252 | var pos = html.indexOf('&'); | ||
253 | if (pos < 0) { return html; } | ||
254 | // Handle numeric entities specially. We can't use functional substitution | ||
255 | // since that doesn't work in older versions of Safari. | ||
256 | // These should be rare since most browsers convert them to normal chars. | ||
257 | for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) { | ||
258 | var end = html.indexOf(';', pos); | ||
259 | if (end >= 0) { | ||
260 | var num = html.substring(pos + 3, end); | ||
261 | var radix = 10; | ||
262 | if (num && num.charAt(0) === 'x') { | ||
263 | num = num.substring(1); | ||
264 | radix = 16; | ||
265 | } | ||
266 | var codePoint = parseInt(num, radix); | ||
267 | if (!isNaN(codePoint)) { | ||
268 | html = (html.substring(0, pos) + String.fromCharCode(codePoint) + | ||
269 | html.substring(end + 1)); | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | |||
274 | return html.replace(pr_ltEnt, '<') | ||
275 | .replace(pr_gtEnt, '>') | ||
276 | .replace(pr_aposEnt, "'") | ||
277 | .replace(pr_quotEnt, '"') | ||
278 | .replace(pr_nbspEnt, ' ') | ||
279 | .replace(pr_ampEnt, '&'); | ||
280 | } | ||
281 | |||
282 | /** is the given node's innerHTML normally unescaped? */ | ||
283 | function isRawContent(node) { | ||
284 | return 'XMP' === node.tagName; | ||
285 | } | ||
286 | |||
287 | var newlineRe = /[\r\n]/g; | ||
288 | /** | ||
289 | * Are newlines and adjacent spaces significant in the given node's innerHTML? | ||
290 | */ | ||
291 | function isPreformatted(node, content) { | ||
292 | // PRE means preformatted, and is a very common case, so don't create | ||
293 | // unnecessary computed style objects. | ||
294 | if ('PRE' === node.tagName) { return true; } | ||
295 | if (!newlineRe.test(content)) { return true; } // Don't care | ||
296 | var whitespace = ''; | ||
297 | // For disconnected nodes, IE has no currentStyle. | ||
298 | if (node.currentStyle) { | ||
299 | whitespace = node.currentStyle.whiteSpace; | ||
300 | } else if (window.getComputedStyle) { | ||
301 | // Firefox makes a best guess if node is disconnected whereas Safari | ||
302 | // returns the empty string. | ||
303 | whitespace = window.getComputedStyle(node, null).whiteSpace; | ||
304 | } | ||
305 | return !whitespace || whitespace === 'pre'; | ||
306 | } | ||
307 | |||
308 | function normalizedHtml(node, out, opt_sortAttrs) { | ||
309 | switch (node.nodeType) { | ||
310 | case 1: // an element | ||
311 | var name = node.tagName.toLowerCase(); | ||
312 | |||
313 | out.push('<', name); | ||
314 | var attrs = node.attributes; | ||
315 | var n = attrs.length; | ||
316 | if (n) { | ||
317 | if (opt_sortAttrs) { | ||
318 | var sortedAttrs = []; | ||
319 | for (var i = n; --i >= 0;) { sortedAttrs[i] = attrs[i]; } | ||
320 | sortedAttrs.sort(function (a, b) { | ||
321 | return (a.name < b.name) ? -1 : a.name === b.name ? 0 : 1; | ||
322 | }); | ||
323 | attrs = sortedAttrs; | ||
324 | } | ||
325 | for (var i = 0; i < n; ++i) { | ||
326 | var attr = attrs[i]; | ||
327 | if (!attr.specified) { continue; } | ||
328 | out.push(' ', attr.name.toLowerCase(), | ||
329 | '="', attribToHtml(attr.value), '"'); | ||
330 | } | ||
331 | } | ||
332 | out.push('>'); | ||
333 | for (var child = node.firstChild; child; child = child.nextSibling) { | ||
334 | normalizedHtml(child, out, opt_sortAttrs); | ||
335 | } | ||
336 | if (node.firstChild || !/^(?:br|link|img)$/.test(name)) { | ||
337 | out.push('<\/', name, '>'); | ||
338 | } | ||
339 | break; | ||
340 | case 3: case 4: // text | ||
341 | out.push(textToHtml(node.nodeValue)); | ||
342 | break; | ||
343 | } | ||
344 | } | ||
345 | |||
346 | /** | ||
347 | * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally | ||
348 | * matches the union o the sets o strings matched d by the input RegExp. | ||
349 | * Since it matches globally, if the input strings have a start-of-input | ||
350 | * anchor (/^.../), it is ignored for the purposes of unioning. | ||
351 | * @param {Array.<RegExp>} regexs non multiline, non-global regexs. | ||
352 | * @return {RegExp} a global regex. | ||
353 | */ | ||
354 | function combinePrefixPatterns(regexs) { | ||
355 | var capturedGroupIndex = 0; | ||
356 | |||
357 | var needToFoldCase = false; | ||
358 | var ignoreCase = false; | ||
359 | for (var i = 0, n = regexs.length; i < n; ++i) { | ||
360 | var regex = regexs[i]; | ||
361 | if (regex.ignoreCase) { | ||
362 | ignoreCase = true; | ||
363 | } else if (/[a-z]/i.test(regex.source.replace( | ||
364 | /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) { | ||
365 | needToFoldCase = true; | ||
366 | ignoreCase = false; | ||
367 | break; | ||
368 | } | ||
369 | } | ||
370 | |||
371 | function decodeEscape(charsetPart) { | ||
372 | if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); } | ||
373 | switch (charsetPart.charAt(1)) { | ||
374 | case 'b': return 8; | ||
375 | case 't': return 9; | ||
376 | case 'n': return 0xa; | ||
377 | case 'v': return 0xb; | ||
378 | case 'f': return 0xc; | ||
379 | case 'r': return 0xd; | ||
380 | case 'u': case 'x': | ||
381 | return parseInt(charsetPart.substring(2), 16) | ||
382 | || charsetPart.charCodeAt(1); | ||
383 | case '0': case '1': case '2': case '3': case '4': | ||
384 | case '5': case '6': case '7': | ||
385 | return parseInt(charsetPart.substring(1), 8); | ||
386 | default: return charsetPart.charCodeAt(1); | ||
387 | } | ||
388 | } | ||
389 | |||
390 | function encodeEscape(charCode) { | ||
391 | if (charCode < 0x20) { | ||
392 | return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16); | ||
393 | } | ||
394 | var ch = String.fromCharCode(charCode); | ||
395 | if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') { | ||
396 | ch = '\\' + ch; | ||
397 | } | ||
398 | return ch; | ||
399 | } | ||
400 | |||
401 | function caseFoldCharset(charSet) { | ||
402 | var charsetParts = charSet.substring(1, charSet.length - 1).match( | ||
403 | new RegExp( | ||
404 | '\\\\u[0-9A-Fa-f]{4}' | ||
405 | + '|\\\\x[0-9A-Fa-f]{2}' | ||
406 | + '|\\\\[0-3][0-7]{0,2}' | ||
407 | + '|\\\\[0-7]{1,2}' | ||
408 | + '|\\\\[\\s\\S]' | ||
409 | + '|-' | ||
410 | + '|[^-\\\\]', | ||
411 | 'g')); | ||
412 | var groups = []; | ||
413 | var ranges = []; | ||
414 | var inverse = charsetParts[0] === '^'; | ||
415 | for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) { | ||
416 | var p = charsetParts[i]; | ||
417 | switch (p) { | ||
418 | case '\\B': case '\\b': | ||
419 | case '\\D': case '\\d': | ||
420 | case '\\S': case '\\s': | ||
421 | case '\\W': case '\\w': | ||
422 | groups.push(p); | ||
423 | continue; | ||
424 | } | ||
425 | var start = decodeEscape(p); | ||
426 | var end; | ||
427 | if (i + 2 < n && '-' === charsetParts[i + 1]) { | ||
428 | end = decodeEscape(charsetParts[i + 2]); | ||
429 | i += 2; | ||
430 | } else { | ||
431 | end = start; | ||
432 | } | ||
433 | ranges.push([start, end]); | ||
434 | // If the range might intersect letters, then expand it. | ||
435 | if (!(end < 65 || start > 122)) { | ||
436 | if (!(end < 65 || start > 90)) { | ||
437 | ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]); | ||
438 | } | ||
439 | if (!(end < 97 || start > 122)) { | ||
440 | ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]); | ||
441 | } | ||
442 | } | ||
443 | } | ||
444 | |||
445 | // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]] | ||
446 | // -> [[1, 12], [14, 14], [16, 17]] | ||
447 | ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); }); | ||
448 | var consolidatedRanges = []; | ||
449 | var lastRange = [NaN, NaN]; | ||
450 | for (var i = 0; i < ranges.length; ++i) { | ||
451 | var range = ranges[i]; | ||
452 | if (range[0] <= lastRange[1] + 1) { | ||
453 | lastRange[1] = Math.max(lastRange[1], range[1]); | ||
454 | } else { | ||
455 | consolidatedRanges.push(lastRange = range); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | var out = ['[']; | ||
460 | if (inverse) { out.push('^'); } | ||
461 | out.push.apply(out, groups); | ||
462 | for (var i = 0; i < consolidatedRanges.length; ++i) { | ||
463 | var range = consolidatedRanges[i]; | ||
464 | out.push(encodeEscape(range[0])); | ||
465 | if (range[1] > range[0]) { | ||
466 | if (range[1] + 1 > range[0]) { out.push('-'); } | ||
467 | out.push(encodeEscape(range[1])); | ||
468 | } | ||
469 | } | ||
470 | out.push(']'); | ||
471 | return out.join(''); | ||
472 | } | ||
473 | |||
474 | function allowAnywhereFoldCaseAndRenumberGroups(regex) { | ||
475 | // Split into character sets, escape sequences, punctuation strings | ||
476 | // like ('(', '(?:', ')', '^'), and runs of characters that do not | ||
477 | // include any of the above. | ||
478 | var parts = regex.source.match( | ||
479 | new RegExp( | ||
480 | '(?:' | ||
481 | + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set | ||
482 | + '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape | ||
483 | + '|\\\\x[A-Fa-f0-9]{2}' // a hex escape | ||
484 | + '|\\\\[0-9]+' // a back-reference or octal escape | ||
485 | + '|\\\\[^ux0-9]' // other escape sequence | ||
486 | + '|\\(\\?[:!=]' // start of a non-capturing group | ||
487 | + '|[\\(\\)\\^]' // start/emd of a group, or line start | ||
488 | + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters | ||
489 | + ')', | ||
490 | 'g')); | ||
491 | var n = parts.length; | ||
492 | |||
493 | // Maps captured group numbers to the number they will occupy in | ||
494 | // the output or to -1 if that has not been determined, or to | ||
495 | // undefined if they need not be capturing in the output. | ||
496 | var capturedGroups = []; | ||
497 | |||
498 | // Walk over and identify back references to build the capturedGroups | ||
499 | // mapping. | ||
500 | for (var i = 0, groupIndex = 0; i < n; ++i) { | ||
501 | var p = parts[i]; | ||
502 | if (p === '(') { | ||
503 | // groups are 1-indexed, so max group index is count of '(' | ||
504 | ++groupIndex; | ||
505 | } else if ('\\' === p.charAt(0)) { | ||
506 | var decimalValue = +p.substring(1); | ||
507 | if (decimalValue && decimalValue <= groupIndex) { | ||
508 | capturedGroups[decimalValue] = -1; | ||
509 | } | ||
510 | } | ||
511 | } | ||
512 | |||
513 | // Renumber groups and reduce capturing groups to non-capturing groups | ||
514 | // where possible. | ||
515 | for (var i = 1; i < capturedGroups.length; ++i) { | ||
516 | if (-1 === capturedGroups[i]) { | ||
517 | capturedGroups[i] = ++capturedGroupIndex; | ||
518 | } | ||
519 | } | ||
520 | for (var i = 0, groupIndex = 0; i < n; ++i) { | ||
521 | var p = parts[i]; | ||
522 | if (p === '(') { | ||
523 | ++groupIndex; | ||
524 | if (capturedGroups[groupIndex] === undefined) { | ||
525 | parts[i] = '(?:'; | ||
526 | } | ||
527 | } else if ('\\' === p.charAt(0)) { | ||
528 | var decimalValue = +p.substring(1); | ||
529 | if (decimalValue && decimalValue <= groupIndex) { | ||
530 | parts[i] = '\\' + capturedGroups[groupIndex]; | ||
531 | } | ||
532 | } | ||
533 | } | ||
534 | |||
535 | // Remove any prefix anchors so that the output will match anywhere. | ||
536 | // ^^ really does mean an anchored match though. | ||
537 | for (var i = 0, groupIndex = 0; i < n; ++i) { | ||
538 | if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; } | ||
539 | } | ||
540 | |||
541 | // Expand letters to groupts to handle mixing of case-sensitive and | ||
542 | // case-insensitive patterns if necessary. | ||
543 | if (regex.ignoreCase && needToFoldCase) { | ||
544 | for (var i = 0; i < n; ++i) { | ||
545 | var p = parts[i]; | ||
546 | var ch0 = p.charAt(0); | ||
547 | if (p.length >= 2 && ch0 === '[') { | ||
548 | parts[i] = caseFoldCharset(p); | ||
549 | } else if (ch0 !== '\\') { | ||
550 | // TODO: handle letters in numeric escapes. | ||
551 | parts[i] = p.replace( | ||
552 | /[a-zA-Z]/g, | ||
553 | function (ch) { | ||
554 | var cc = ch.charCodeAt(0); | ||
555 | return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']'; | ||
556 | }); | ||
557 | } | ||
558 | } | ||
559 | } | ||
560 | |||
561 | return parts.join(''); | ||
562 | } | ||
563 | |||
564 | var rewritten = []; | ||
565 | for (var i = 0, n = regexs.length; i < n; ++i) { | ||
566 | var regex = regexs[i]; | ||
567 | if (regex.global || regex.multiline) { throw new Error('' + regex); } | ||
568 | rewritten.push( | ||
569 | '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')'); | ||
570 | } | ||
571 | |||
572 | return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g'); | ||
573 | } | ||
574 | |||
575 | var PR_innerHtmlWorks = null; | ||
576 | function getInnerHtml(node) { | ||
577 | // inner html is hopelessly broken in Safari 2.0.4 when the content is | ||
578 | // an html description of well formed XML and the containing tag is a PRE | ||
579 | // tag, so we detect that case and emulate innerHTML. | ||
580 | if (null === PR_innerHtmlWorks) { | ||
581 | var testNode = document.createElement('PRE'); | ||
582 | testNode.appendChild( | ||
583 | document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />')); | ||
584 | PR_innerHtmlWorks = !/</.test(testNode.innerHTML); | ||
585 | } | ||
586 | |||
587 | if (PR_innerHtmlWorks) { | ||
588 | var content = node.innerHTML; | ||
589 | // XMP tags contain unescaped entities so require special handling. | ||
590 | if (isRawContent(node)) { | ||
591 | content = textToHtml(content); | ||
592 | } else if (!isPreformatted(node, content)) { | ||
593 | content = content.replace(/(<br\s*\/?>)[\r\n]+/g, '$1') | ||
594 | .replace(/(?:[\r\n]+[ \t]*)+/g, ' '); | ||
595 | } | ||
596 | return content; | ||
597 | } | ||
598 | |||
599 | var out = []; | ||
600 | for (var child = node.firstChild; child; child = child.nextSibling) { | ||
601 | normalizedHtml(child, out); | ||
602 | } | ||
603 | return out.join(''); | ||
604 | } | ||
605 | |||
606 | /** returns a function that expand tabs to spaces. This function can be fed | ||
607 | * successive chunks of text, and will maintain its own internal state to | ||
608 | * keep track of how tabs are expanded. | ||
609 | * @return {function (string) : string} a function that takes | ||
610 | * plain text and return the text with tabs expanded. | ||
611 | * @private | ||
612 | */ | ||
613 | function makeTabExpander(tabWidth) { | ||
614 | var SPACES = ' '; | ||
615 | var charInLine = 0; | ||
616 | |||
617 | return function (plainText) { | ||
618 | // walk over each character looking for tabs and newlines. | ||
619 | // On tabs, expand them. On newlines, reset charInLine. | ||
620 | // Otherwise increment charInLine | ||
621 | var out = null; | ||
622 | var pos = 0; | ||
623 | for (var i = 0, n = plainText.length; i < n; ++i) { | ||
624 | var ch = plainText.charAt(i); | ||
625 | |||
626 | switch (ch) { | ||
627 | case '\t': | ||
628 | if (!out) { out = []; } | ||
629 | out.push(plainText.substring(pos, i)); | ||
630 | // calculate how much space we need in front of this part | ||
631 | // nSpaces is the amount of padding -- the number of spaces needed | ||
632 | // to move us to the next column, where columns occur at factors of | ||
633 | // tabWidth. | ||
634 | var nSpaces = tabWidth - (charInLine % tabWidth); | ||
635 | charInLine += nSpaces; | ||
636 | for (; nSpaces >= 0; nSpaces -= SPACES.length) { | ||
637 | out.push(SPACES.substring(0, nSpaces)); | ||
638 | } | ||
639 | pos = i + 1; | ||
640 | break; | ||
641 | case '\n': | ||
642 | charInLine = 0; | ||
643 | break; | ||
644 | default: | ||
645 | ++charInLine; | ||
646 | } | ||
647 | } | ||
648 | if (!out) { return plainText; } | ||
649 | out.push(plainText.substring(pos)); | ||
650 | return out.join(''); | ||
651 | }; | ||
652 | } | ||
653 | |||
654 | var pr_chunkPattern = new RegExp( | ||
655 | '[^<]+' // A run of characters other than '<' | ||
656 | + '|<\!--[\\s\\S]*?--\>' // an HTML comment | ||
657 | + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>' // a CDATA section | ||
658 | // a probable tag that should not be highlighted | ||
659 | + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>' | ||
660 | + '|<', // A '<' that does not begin a larger chunk | ||
661 | 'g'); | ||
662 | var pr_commentPrefix = /^<\!--/; | ||
663 | var pr_cdataPrefix = /^<!\[CDATA\[/; | ||
664 | var pr_brPrefix = /^<br\b/i; | ||
665 | var pr_tagNameRe = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/; | ||
666 | |||
667 | /** split markup into chunks of html tags (style null) and | ||
668 | * plain text (style {@link #PR_PLAIN}), converting tags which are | ||
669 | * significant for tokenization (<br>) into their textual equivalent. | ||
670 | * | ||
671 | * @param {string} s html where whitespace is considered significant. | ||
672 | * @return {Object} source code and extracted tags. | ||
673 | * @private | ||
674 | */ | ||
675 | function extractTags(s) { | ||
676 | // since the pattern has the 'g' modifier and defines no capturing groups, | ||
677 | // this will return a list of all chunks which we then classify and wrap as | ||
678 | // PR_Tokens | ||
679 | var matches = s.match(pr_chunkPattern); | ||
680 | var sourceBuf = []; | ||
681 | var sourceBufLen = 0; | ||
682 | var extractedTags = []; | ||
683 | if (matches) { | ||
684 | for (var i = 0, n = matches.length; i < n; ++i) { | ||
685 | var match = matches[i]; | ||
686 | if (match.length > 1 && match.charAt(0) === '<') { | ||
687 | if (pr_commentPrefix.test(match)) { continue; } | ||
688 | if (pr_cdataPrefix.test(match)) { | ||
689 | // strip CDATA prefix and suffix. Don't unescape since it's CDATA | ||
690 | sourceBuf.push(match.substring(9, match.length - 3)); | ||
691 | sourceBufLen += match.length - 12; | ||
692 | } else if (pr_brPrefix.test(match)) { | ||
693 | // <br> tags are lexically significant so convert them to text. | ||
694 | // This is undone later. | ||
695 | sourceBuf.push('\n'); | ||
696 | ++sourceBufLen; | ||
697 | } else { | ||
698 | if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) { | ||
699 | // A <span class="nocode"> will start a section that should be | ||
700 | // ignored. Continue walking the list until we see a matching end | ||
701 | // tag. | ||
702 | var name = match.match(pr_tagNameRe)[2]; | ||
703 | var depth = 1; | ||
704 | var j; | ||
705 | end_tag_loop: | ||
706 | for (j = i + 1; j < n; ++j) { | ||
707 | var name2 = matches[j].match(pr_tagNameRe); | ||
708 | if (name2 && name2[2] === name) { | ||
709 | if (name2[1] === '/') { | ||
710 | if (--depth === 0) { break end_tag_loop; } | ||
711 | } else { | ||
712 | ++depth; | ||
713 | } | ||
714 | } | ||
715 | } | ||
716 | if (j < n) { | ||
717 | extractedTags.push( | ||
718 | sourceBufLen, matches.slice(i, j + 1).join('')); | ||
719 | i = j; | ||
720 | } else { // Ignore unclosed sections. | ||
721 | extractedTags.push(sourceBufLen, match); | ||
722 | } | ||
723 | } else { | ||
724 | extractedTags.push(sourceBufLen, match); | ||
725 | } | ||
726 | } | ||
727 | } else { | ||
728 | var literalText = htmlToText(match); | ||
729 | sourceBuf.push(literalText); | ||
730 | sourceBufLen += literalText.length; | ||
731 | } | ||
732 | } | ||
733 | } | ||
734 | return { source: sourceBuf.join(''), tags: extractedTags }; | ||
735 | } | ||
736 | |||
737 | /** True if the given tag contains a class attribute with the nocode class. */ | ||
738 | function isNoCodeTag(tag) { | ||
739 | return !!tag | ||
740 | // First canonicalize the representation of attributes | ||
741 | .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g, | ||
742 | ' $1="$2$3$4"') | ||
743 | // Then look for the attribute we want. | ||
744 | .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/); | ||
745 | } | ||
746 | |||
747 | /** | ||
748 | * Apply the given language handler to sourceCode and add the resulting | ||
749 | * decorations to out. | ||
750 | * @param {number} basePos the index of sourceCode within the chunk of source | ||
751 | * whose decorations are already present on out. | ||
752 | */ | ||
753 | function appendDecorations(basePos, sourceCode, langHandler, out) { | ||
754 | if (!sourceCode) { return; } | ||
755 | var job = { | ||
756 | source: sourceCode, | ||
757 | basePos: basePos | ||
758 | }; | ||
759 | langHandler(job); | ||
760 | out.push.apply(out, job.decorations); | ||
761 | } | ||
762 | |||
763 | /** Given triples of [style, pattern, context] returns a lexing function, | ||
764 | * The lexing function interprets the patterns to find token boundaries and | ||
765 | * returns a decoration list of the form | ||
766 | * [index_0, style_0, index_1, style_1, ..., index_n, style_n] | ||
767 | * where index_n is an index into the sourceCode, and style_n is a style | ||
768 | * constant like PR_PLAIN. index_n-1 <= index_n, and style_n-1 applies to | ||
769 | * all characters in sourceCode[index_n-1:index_n]. | ||
770 | * | ||
771 | * The stylePatterns is a list whose elements have the form | ||
772 | * [style : string, pattern : RegExp, DEPRECATED, shortcut : string]. | ||
773 | * | ||
774 | * Style is a style constant like PR_PLAIN, or can be a string of the | ||
775 | * form 'lang-FOO', where FOO is a language extension describing the | ||
776 | * language of the portion of the token in $1 after pattern executes. | ||
777 | * E.g., if style is 'lang-lisp', and group 1 contains the text | ||
778 | * '(hello (world))', then that portion of the token will be passed to the | ||
779 | * registered lisp handler for formatting. | ||
780 | * The text before and after group 1 will be restyled using this decorator | ||
781 | * so decorators should take care that this doesn't result in infinite | ||
782 | * recursion. For example, the HTML lexer rule for SCRIPT elements looks | ||
783 | * something like ['lang-js', /<[s]cript>(.+?)<\/script>/]. This may match | ||
784 | * '<script>foo()<\/script>', which would cause the current decorator to | ||
785 | * be called with '<script>' which would not match the same rule since | ||
786 | * group 1 must not be empty, so it would be instead styled as PR_TAG by | ||
787 | * the generic tag rule. The handler registered for the 'js' extension would | ||
788 | * then be called with 'foo()', and finally, the current decorator would | ||
789 | * be called with '<\/script>' which would not match the original rule and | ||
790 | * so the generic tag rule would identify it as a tag. | ||
791 | * | ||
792 | * Pattern must only match prefixes, and if it matches a prefix, then that | ||
793 | * match is considered a token with the same style. | ||
794 | * | ||
795 | * Context is applied to the last non-whitespace, non-comment token | ||
796 | * recognized. | ||
797 | * | ||
798 | * Shortcut is an optional string of characters, any of which, if the first | ||
799 | * character, gurantee that this pattern and only this pattern matches. | ||
800 | * | ||
801 | * @param {Array} shortcutStylePatterns patterns that always start with | ||
802 | * a known character. Must have a shortcut string. | ||
803 | * @param {Array} fallthroughStylePatterns patterns that will be tried in | ||
804 | * order if the shortcut ones fail. May have shortcuts. | ||
805 | * | ||
806 | * @return {function (Object)} a | ||
807 | * function that takes source code and returns a list of decorations. | ||
808 | */ | ||
809 | function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) { | ||
810 | var shortcuts = {}; | ||
811 | var tokenizer; | ||
812 | (function () { | ||
813 | var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns); | ||
814 | var allRegexs = []; | ||
815 | var regexKeys = {}; | ||
816 | for (var i = 0, n = allPatterns.length; i < n; ++i) { | ||
817 | var patternParts = allPatterns[i]; | ||
818 | var shortcutChars = patternParts[3]; | ||
819 | if (shortcutChars) { | ||
820 | for (var c = shortcutChars.length; --c >= 0;) { | ||
821 | shortcuts[shortcutChars.charAt(c)] = patternParts; | ||
822 | } | ||
823 | } | ||
824 | var regex = patternParts[1]; | ||
825 | var k = '' + regex; | ||
826 | if (!regexKeys.hasOwnProperty(k)) { | ||
827 | allRegexs.push(regex); | ||
828 | regexKeys[k] = null; | ||
829 | } | ||
830 | } | ||
831 | allRegexs.push(/[\0-\uffff]/); | ||
832 | tokenizer = combinePrefixPatterns(allRegexs); | ||
833 | })(); | ||
834 | |||
835 | var nPatterns = fallthroughStylePatterns.length; | ||
836 | var notWs = /\S/; | ||
837 | |||
838 | /** | ||
839 | * Lexes job.source and produces an output array job.decorations of style | ||
840 | * classes preceded by the position at which they start in job.source in | ||
841 | * order. | ||
842 | * | ||
843 | * @param {Object} job an object like {@code | ||
844 | * source: {string} sourceText plain text, | ||
845 | * basePos: {int} position of job.source in the larger chunk of | ||
846 | * sourceCode. | ||
847 | * } | ||
848 | */ | ||
849 | var decorate = function (job) { | ||
850 | var sourceCode = job.source, basePos = job.basePos; | ||
851 | /** Even entries are positions in source in ascending order. Odd enties | ||
852 | * are style markers (e.g., PR_COMMENT) that run from that position until | ||
853 | * the end. | ||
854 | * @type {Array.<number|string>} | ||
855 | */ | ||
856 | var decorations = [basePos, PR_PLAIN]; | ||
857 | var pos = 0; // index into sourceCode | ||
858 | var tokens = sourceCode.match(tokenizer) || []; | ||
859 | var styleCache = {}; | ||
860 | |||
861 | for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) { | ||
862 | var token = tokens[ti]; | ||
863 | var style = styleCache[token]; | ||
864 | var match = void 0; | ||
865 | |||
866 | var isEmbedded; | ||
867 | if (typeof style === 'string') { | ||
868 | isEmbedded = false; | ||
869 | } else { | ||
870 | var patternParts = shortcuts[token.charAt(0)]; | ||
871 | if (patternParts) { | ||
872 | match = token.match(patternParts[1]); | ||
873 | style = patternParts[0]; | ||
874 | } else { | ||
875 | for (var i = 0; i < nPatterns; ++i) { | ||
876 | patternParts = fallthroughStylePatterns[i]; | ||
877 | match = token.match(patternParts[1]); | ||
878 | if (match) { | ||
879 | style = patternParts[0]; | ||
880 | break; | ||
881 | } | ||
882 | } | ||
883 | |||
884 | if (!match) { // make sure that we make progress | ||
885 | style = PR_PLAIN; | ||
886 | } | ||
887 | } | ||
888 | |||
889 | isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5); | ||
890 | if (isEmbedded && !(match && typeof match[1] === 'string')) { | ||
891 | isEmbedded = false; | ||
892 | style = PR_SOURCE; | ||
893 | } | ||
894 | |||
895 | if (!isEmbedded) { styleCache[token] = style; } | ||
896 | } | ||
897 | |||
898 | var tokenStart = pos; | ||
899 | pos += token.length; | ||
900 | |||
901 | if (!isEmbedded) { | ||
902 | decorations.push(basePos + tokenStart, style); | ||
903 | } else { // Treat group 1 as an embedded block of source code. | ||
904 | var embeddedSource = match[1]; | ||
905 | var embeddedSourceStart = token.indexOf(embeddedSource); | ||
906 | var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length; | ||
907 | if (match[2]) { | ||
908 | // If embeddedSource can be blank, then it would match at the | ||
909 | // beginning which would cause us to infinitely recurse on the | ||
910 | // entire token, so we catch the right context in match[2]. | ||
911 | embeddedSourceEnd = token.length - match[2].length; | ||
912 | embeddedSourceStart = embeddedSourceEnd - embeddedSource.length; | ||
913 | } | ||
914 | var lang = style.substring(5); | ||
915 | // Decorate the left of the embedded source | ||
916 | appendDecorations( | ||
917 | basePos + tokenStart, | ||
918 | token.substring(0, embeddedSourceStart), | ||
919 | decorate, decorations); | ||
920 | // Decorate the embedded source | ||
921 | appendDecorations( | ||
922 | basePos + tokenStart + embeddedSourceStart, | ||
923 | embeddedSource, | ||
924 | langHandlerForExtension(lang, embeddedSource), | ||
925 | decorations); | ||
926 | // Decorate the right of the embedded section | ||
927 | appendDecorations( | ||
928 | basePos + tokenStart + embeddedSourceEnd, | ||
929 | token.substring(embeddedSourceEnd), | ||
930 | decorate, decorations); | ||
931 | } | ||
932 | } | ||
933 | job.decorations = decorations; | ||
934 | }; | ||
935 | return decorate; | ||
936 | } | ||
937 | |||
938 | /** returns a function that produces a list of decorations from source text. | ||
939 | * | ||
940 | * This code treats ", ', and ` as string delimiters, and \ as a string | ||
941 | * escape. It does not recognize perl's qq() style strings. | ||
942 | * It has no special handling for double delimiter escapes as in basic, or | ||
943 | * the tripled delimiters used in python, but should work on those regardless | ||
944 | * although in those cases a single string literal may be broken up into | ||
945 | * multiple adjacent string literals. | ||
946 | * | ||
947 | * It recognizes C, C++, and shell style comments. | ||
948 | * | ||
949 | * @param {Object} options a set of optional parameters. | ||
950 | * @return {function (Object)} a function that examines the source code | ||
951 | * in the input job and builds the decoration list. | ||
952 | */ | ||
953 | function sourceDecorator(options) { | ||
954 | var shortcutStylePatterns = [], fallthroughStylePatterns = []; | ||
955 | if (options['tripleQuotedStrings']) { | ||
956 | // '''multi-line-string''', 'single-line-string', and double-quoted | ||
957 | shortcutStylePatterns.push( | ||
958 | [PR_STRING, /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/, | ||
959 | null, '\'"']); | ||
960 | } else if (options['multiLineStrings']) { | ||
961 | // 'multi-line-string', "multi-line-string" | ||
962 | shortcutStylePatterns.push( | ||
963 | [PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/, | ||
964 | null, '\'"`']); | ||
965 | } else { | ||
966 | // 'single-line-string', "single-line-string" | ||
967 | shortcutStylePatterns.push( | ||
968 | [PR_STRING, | ||
969 | /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/, | ||
970 | null, '"\'']); | ||
971 | } | ||
972 | if (options['verbatimStrings']) { | ||
973 | // verbatim-string-literal production from the C# grammar. See issue 93. | ||
974 | fallthroughStylePatterns.push( | ||
975 | [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]); | ||
976 | } | ||
977 | if (options['hashComments']) { | ||
978 | if (options['cStyleComments']) { | ||
979 | // Stop C preprocessor declarations at an unclosed open comment | ||
980 | shortcutStylePatterns.push( | ||
981 | [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/, | ||
982 | null, '#']); | ||
983 | fallthroughStylePatterns.push( | ||
984 | [PR_STRING, | ||
985 | /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/, | ||
986 | null]); | ||
987 | } else { | ||
988 | shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']); | ||
989 | } | ||
990 | } | ||
991 | if (options['cStyleComments']) { | ||
992 | fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]); | ||
993 | fallthroughStylePatterns.push( | ||
994 | [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]); | ||
995 | } | ||
996 | if (options['regexLiterals']) { | ||
997 | var REGEX_LITERAL = ( | ||
998 | // A regular expression literal starts with a slash that is | ||
999 | // not followed by * or / so that it is not confused with | ||
1000 | // comments. | ||
1001 | '/(?=[^/*])' | ||
1002 | // and then contains any number of raw characters, | ||
1003 | + '(?:[^/\\x5B\\x5C]' | ||
1004 | // escape sequences (\x5C), | ||
1005 | + '|\\x5C[\\s\\S]' | ||
1006 | // or non-nesting character sets (\x5B\x5D); | ||
1007 | + '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+' | ||
1008 | // finally closed by a /. | ||
1009 | + '/'); | ||
1010 | fallthroughStylePatterns.push( | ||
1011 | ['lang-regex', | ||
1012 | new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')') | ||
1013 | ]); | ||
1014 | } | ||
1015 | |||
1016 | var keywords = options['keywords'].replace(/^\s+|\s+$/g, ''); | ||
1017 | if (keywords.length) { | ||
1018 | fallthroughStylePatterns.push( | ||
1019 | [PR_KEYWORD, | ||
1020 | new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]); | ||
1021 | } | ||
1022 | |||
1023 | shortcutStylePatterns.push([PR_PLAIN, /^\s+/, null, ' \r\n\t\xA0']); | ||
1024 | fallthroughStylePatterns.push( | ||
1025 | // TODO(mikesamuel): recognize non-latin letters and numerals in idents | ||
1026 | [PR_LITERAL, /^@[a-z_$][a-z_$@0-9]*/i, null], | ||
1027 | [PR_TYPE, /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null], | ||
1028 | [PR_PLAIN, /^[a-z_$][a-z_$@0-9]*/i, null], | ||
1029 | [PR_LITERAL, | ||
1030 | new RegExp( | ||
1031 | '^(?:' | ||
1032 | // A hex number | ||
1033 | + '0x[a-f0-9]+' | ||
1034 | // or an octal or decimal number, | ||
1035 | + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)' | ||
1036 | // possibly in scientific notation | ||
1037 | + '(?:e[+\\-]?\\d+)?' | ||
1038 | + ')' | ||
1039 | // with an optional modifier like UL for unsigned long | ||
1040 | + '[a-z]*', 'i'), | ||
1041 | null, '0123456789'], | ||
1042 | [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#]*/, null]); | ||
1043 | |||
1044 | return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns); | ||
1045 | } | ||
1046 | |||
1047 | var decorateSource = sourceDecorator({ | ||
1048 | 'keywords': ALL_KEYWORDS, | ||
1049 | 'hashComments': true, | ||
1050 | 'cStyleComments': true, | ||
1051 | 'multiLineStrings': true, | ||
1052 | 'regexLiterals': true | ||
1053 | }); | ||
1054 | |||
1055 | /** Breaks {@code job.source} around style boundaries in | ||
1056 | * {@code job.decorations} while re-interleaving {@code job.extractedTags}, | ||
1057 | * and leaves the result in {@code job.prettyPrintedHtml}. | ||
1058 | * @param {Object} job like { | ||
1059 | * source: {string} source as plain text, | ||
1060 | * extractedTags: {Array.<number|string>} extractedTags chunks of raw | ||
1061 | * html preceded by their position in {@code job.source} | ||
1062 | * in order | ||
1063 | * decorations: {Array.<number|string} an array of style classes preceded | ||
1064 | * by the position at which they start in job.source in order | ||
1065 | * } | ||
1066 | * @private | ||
1067 | */ | ||
1068 | function recombineTagsAndDecorations(job) { | ||
1069 | var sourceText = job.source; | ||
1070 | var extractedTags = job.extractedTags; | ||
1071 | var decorations = job.decorations; | ||
1072 | |||
1073 | var html = []; | ||
1074 | // index past the last char in sourceText written to html | ||
1075 | var outputIdx = 0; | ||
1076 | |||
1077 | var openDecoration = null; | ||
1078 | var currentDecoration = null; | ||
1079 | var tagPos = 0; // index into extractedTags | ||
1080 | var decPos = 0; // index into decorations | ||
1081 | var tabExpander = makeTabExpander(window['PR_TAB_WIDTH']); | ||
1082 | |||
1083 | var adjacentSpaceRe = /([\r\n ]) /g; | ||
1084 | var startOrSpaceRe = /(^| ) /gm; | ||
1085 | var newlineRe = /\r\n?|\n/g; | ||
1086 | var trailingSpaceRe = /[ \r\n]$/; | ||
1087 | var lastWasSpace = true; // the last text chunk emitted ended with a space. | ||
1088 | |||
1089 | // See bug 71 and http://stackoverflow.com/questions/136443/why-doesnt-ie7- | ||
1090 | var isIE678 = window['_pr_isIE6'](); | ||
1091 | var lineBreakHtml = ( | ||
1092 | isIE678 | ||
1093 | ? (job.sourceNode.tagName === 'PRE' | ||
1094 | // Use line feeds instead of <br>s so that copying and pasting works | ||
1095 | // on IE. | ||
1096 | // Doing this on other browsers breaks lots of stuff since \r\n is | ||
1097 | // treated as two newlines on Firefox. | ||
1098 | ? (isIE678 === 6 ? ' \r\n' : | ||
1099 | isIE678 === 7 ? ' <br>\r' : ' \r') | ||
1100 | // IE collapses multiple adjacent <br>s into 1 line break. | ||
1101 | // Prefix every newline with ' ' to prevent such behavior. | ||
1102 | // is the same as   but works in XML as well as HTML. | ||
1103 | : ' <br />') | ||
1104 | : '<br />'); | ||
1105 | |||
1106 | // Look for a class like linenums or linenums:<n> where <n> is the 1-indexed | ||
1107 | // number of the first line. | ||
1108 | var numberLines = job.sourceNode.className.match(/\blinenums\b(?::(\d+))?/); | ||
1109 | var lineBreaker; | ||
1110 | if (numberLines) { | ||
1111 | var lineBreaks = []; | ||
1112 | for (var i = 0; i < 10; ++i) { | ||
1113 | lineBreaks[i] = lineBreakHtml + '</li><li class="L' + i + '">'; | ||
1114 | } | ||
1115 | var lineNum = numberLines[1] && numberLines[1].length | ||
1116 | ? numberLines[1] - 1 : 0; // Lines are 1-indexed | ||
1117 | html.push('<ol class="linenums"><li class="L', (lineNum) % 10, '"'); | ||
1118 | if (lineNum) { | ||
1119 | html.push(' value="', lineNum + 1, '"'); | ||
1120 | } | ||
1121 | html.push('>'); | ||
1122 | lineBreaker = function () { | ||
1123 | var lb = lineBreaks[++lineNum % 10]; | ||
1124 | // If a decoration is open, we need to close it before closing a list-item | ||
1125 | // and reopen it on the other side of the list item. | ||
1126 | return openDecoration | ||
1127 | ? ('</span>' + lb + '<span class="' + openDecoration + '">') : lb; | ||
1128 | }; | ||
1129 | } else { | ||
1130 | lineBreaker = lineBreakHtml; | ||
1131 | } | ||
1132 | |||
1133 | // A helper function that is responsible for opening sections of decoration | ||
1134 | // and outputing properly escaped chunks of source | ||
1135 | function emitTextUpTo(sourceIdx) { | ||
1136 | if (sourceIdx > outputIdx) { | ||
1137 | if (openDecoration && openDecoration !== currentDecoration) { | ||
1138 | // Close the current decoration | ||
1139 | html.push('</span>'); | ||
1140 | openDecoration = null; | ||
1141 | } | ||
1142 | if (!openDecoration && currentDecoration) { | ||
1143 | openDecoration = currentDecoration; | ||
1144 | html.push('<span class="', openDecoration, '">'); | ||
1145 | } | ||
1146 | // This interacts badly with some wikis which introduces paragraph tags | ||
1147 | // into pre blocks for some strange reason. | ||
1148 | // It's necessary for IE though which seems to lose the preformattedness | ||
1149 | // of <pre> tags when their innerHTML is assigned. | ||
1150 | // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html | ||
1151 | // and it serves to undo the conversion of <br>s to newlines done in | ||
1152 | // chunkify. | ||
1153 | var htmlChunk = textToHtml( | ||
1154 | tabExpander(sourceText.substring(outputIdx, sourceIdx))) | ||
1155 | .replace(lastWasSpace | ||
1156 | ? startOrSpaceRe | ||
1157 | : adjacentSpaceRe, '$1 '); | ||
1158 | // Keep track of whether we need to escape space at the beginning of the | ||
1159 | // next chunk. | ||
1160 | lastWasSpace = trailingSpaceRe.test(htmlChunk); | ||
1161 | html.push(htmlChunk.replace(newlineRe, lineBreaker)); | ||
1162 | outputIdx = sourceIdx; | ||
1163 | } | ||
1164 | } | ||
1165 | |||
1166 | while (true) { | ||
1167 | // Determine if we're going to consume a tag this time around. Otherwise | ||
1168 | // we consume a decoration or exit. | ||
1169 | var outputTag; | ||
1170 | if (tagPos < extractedTags.length) { | ||
1171 | if (decPos < decorations.length) { | ||
1172 | // Pick one giving preference to extractedTags since we shouldn't open | ||
1173 | // a new style that we're going to have to immediately close in order | ||
1174 | // to output a tag. | ||
1175 | outputTag = extractedTags[tagPos] <= decorations[decPos]; | ||
1176 | } else { | ||
1177 | outputTag = true; | ||
1178 | } | ||
1179 | } else { | ||
1180 | outputTag = false; | ||
1181 | } | ||
1182 | // Consume either a decoration or a tag or exit. | ||
1183 | if (outputTag) { | ||
1184 | emitTextUpTo(extractedTags[tagPos]); | ||
1185 | if (openDecoration) { | ||
1186 | // Close the current decoration | ||
1187 | html.push('</span>'); | ||
1188 | openDecoration = null; | ||
1189 | } | ||
1190 | html.push(extractedTags[tagPos + 1]); | ||
1191 | tagPos += 2; | ||
1192 | } else if (decPos < decorations.length) { | ||
1193 | emitTextUpTo(decorations[decPos]); | ||
1194 | currentDecoration = decorations[decPos + 1]; | ||
1195 | decPos += 2; | ||
1196 | } else { | ||
1197 | break; | ||
1198 | } | ||
1199 | } | ||
1200 | emitTextUpTo(sourceText.length); | ||
1201 | if (openDecoration) { | ||
1202 | html.push('</span>'); | ||
1203 | } | ||
1204 | if (numberLines) { html.push('</li></ol>'); } | ||
1205 | job.prettyPrintedHtml = html.join(''); | ||
1206 | } | ||
1207 | |||
1208 | /** Maps language-specific file extensions to handlers. */ | ||
1209 | var langHandlerRegistry = {}; | ||
1210 | /** Register a language handler for the given file extensions. | ||
1211 | * @param {function (Object)} handler a function from source code to a list | ||
1212 | * of decorations. Takes a single argument job which describes the | ||
1213 | * state of the computation. The single parameter has the form | ||
1214 | * {@code { | ||
1215 | * source: {string} as plain text. | ||
1216 | * decorations: {Array.<number|string>} an array of style classes | ||
1217 | * preceded by the position at which they start in | ||
1218 | * job.source in order. | ||
1219 | * The language handler should assigned this field. | ||
1220 | * basePos: {int} the position of source in the larger source chunk. | ||
1221 | * All positions in the output decorations array are relative | ||
1222 | * to the larger source chunk. | ||
1223 | * } } | ||
1224 | * @param {Array.<string>} fileExtensions | ||
1225 | */ | ||
1226 | function registerLangHandler(handler, fileExtensions) { | ||
1227 | for (var i = fileExtensions.length; --i >= 0;) { | ||
1228 | var ext = fileExtensions[i]; | ||
1229 | if (!langHandlerRegistry.hasOwnProperty(ext)) { | ||
1230 | langHandlerRegistry[ext] = handler; | ||
1231 | } else if ('console' in window) { | ||
1232 | console['warn']('cannot override language handler %s', ext); | ||
1233 | } | ||
1234 | } | ||
1235 | } | ||
1236 | function langHandlerForExtension(extension, source) { | ||
1237 | if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) { | ||
1238 | // Treat it as markup if the first non whitespace character is a < and | ||
1239 | // the last non-whitespace character is a >. | ||
1240 | extension = /^\s*</.test(source) | ||
1241 | ? 'default-markup' | ||
1242 | : 'default-code'; | ||
1243 | } | ||
1244 | return langHandlerRegistry[extension]; | ||
1245 | } | ||
1246 | registerLangHandler(decorateSource, ['default-code']); | ||
1247 | registerLangHandler( | ||
1248 | createSimpleLexer( | ||
1249 | [], | ||
1250 | [ | ||
1251 | [PR_PLAIN, /^[^<?]+/], | ||
1252 | [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/], | ||
1253 | [PR_COMMENT, /^<\!--[\s\S]*?(?:-\->|$)/], | ||
1254 | // Unescaped content in an unknown language | ||
1255 | ['lang-', /^<\?([\s\S]+?)(?:\?>|$)/], | ||
1256 | ['lang-', /^<%([\s\S]+?)(?:%>|$)/], | ||
1257 | [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/], | ||
1258 | ['lang-', /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i], | ||
1259 | // Unescaped content in javascript. (Or possibly vbscript). | ||
1260 | ['lang-js', /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i], | ||
1261 | // Contains unescaped stylesheet content | ||
1262 | ['lang-css', /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i], | ||
1263 | ['lang-in.tag', /^(<\/?[a-z][^<>]*>)/i] | ||
1264 | ]), | ||
1265 | ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']); | ||
1266 | registerLangHandler( | ||
1267 | createSimpleLexer( | ||
1268 | [ | ||
1269 | [PR_PLAIN, /^[\s]+/, null, ' \t\r\n'], | ||
1270 | [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\''] | ||
1271 | ], | ||
1272 | [ | ||
1273 | [PR_TAG, /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i], | ||
1274 | [PR_ATTRIB_NAME, /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i], | ||
1275 | ['lang-uq.val', /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/], | ||
1276 | [PR_PUNCTUATION, /^[=<>\/]+/], | ||
1277 | ['lang-js', /^on\w+\s*=\s*\"([^\"]+)\"/i], | ||
1278 | ['lang-js', /^on\w+\s*=\s*\'([^\']+)\'/i], | ||
1279 | ['lang-js', /^on\w+\s*=\s*([^\"\'>\s]+)/i], | ||
1280 | ['lang-css', /^style\s*=\s*\"([^\"]+)\"/i], | ||
1281 | ['lang-css', /^style\s*=\s*\'([^\']+)\'/i], | ||
1282 | ['lang-css', /^style\s*=\s*([^\"\'>\s]+)/i] | ||
1283 | ]), | ||
1284 | ['in.tag']); | ||
1285 | registerLangHandler( | ||
1286 | createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']); | ||
1287 | registerLangHandler(sourceDecorator({ | ||
1288 | 'keywords': CPP_KEYWORDS, | ||
1289 | 'hashComments': true, | ||
1290 | 'cStyleComments': true | ||
1291 | }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']); | ||
1292 | registerLangHandler(sourceDecorator({ | ||
1293 | 'keywords': 'null true false' | ||
1294 | }), ['json']); | ||
1295 | registerLangHandler(sourceDecorator({ | ||
1296 | 'keywords': CSHARP_KEYWORDS, | ||
1297 | 'hashComments': true, | ||
1298 | 'cStyleComments': true, | ||
1299 | 'verbatimStrings': true | ||
1300 | }), ['cs']); | ||
1301 | registerLangHandler(sourceDecorator({ | ||
1302 | 'keywords': JAVA_KEYWORDS, | ||
1303 | 'cStyleComments': true | ||
1304 | }), ['java']); | ||
1305 | registerLangHandler(sourceDecorator({ | ||
1306 | 'keywords': SH_KEYWORDS, | ||
1307 | 'hashComments': true, | ||
1308 | 'multiLineStrings': true | ||
1309 | }), ['bsh', 'csh', 'sh']); | ||
1310 | registerLangHandler(sourceDecorator({ | ||
1311 | 'keywords': PYTHON_KEYWORDS, | ||
1312 | 'hashComments': true, | ||
1313 | 'multiLineStrings': true, | ||
1314 | 'tripleQuotedStrings': true | ||
1315 | }), ['cv', 'py']); | ||
1316 | registerLangHandler(sourceDecorator({ | ||
1317 | 'keywords': PERL_KEYWORDS, | ||
1318 | 'hashComments': true, | ||
1319 | 'multiLineStrings': true, | ||
1320 | 'regexLiterals': true | ||
1321 | }), ['perl', 'pl', 'pm']); | ||
1322 | registerLangHandler(sourceDecorator({ | ||
1323 | 'keywords': RUBY_KEYWORDS, | ||
1324 | 'hashComments': true, | ||
1325 | 'multiLineStrings': true, | ||
1326 | 'regexLiterals': true | ||
1327 | }), ['rb']); | ||
1328 | registerLangHandler(sourceDecorator({ | ||
1329 | 'keywords': JSCRIPT_KEYWORDS, | ||
1330 | 'cStyleComments': true, | ||
1331 | 'regexLiterals': true | ||
1332 | }), ['js']); | ||
1333 | registerLangHandler( | ||
1334 | createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']); | ||
1335 | |||
1336 | function applyDecorator(job) { | ||
1337 | var sourceCodeHtml = job.sourceCodeHtml; | ||
1338 | var opt_langExtension = job.langExtension; | ||
1339 | |||
1340 | // Prepopulate output in case processing fails with an exception. | ||
1341 | job.prettyPrintedHtml = sourceCodeHtml; | ||
1342 | |||
1343 | try { | ||
1344 | // Extract tags, and convert the source code to plain text. | ||
1345 | var sourceAndExtractedTags = extractTags(sourceCodeHtml); | ||
1346 | /** Plain text. @type {string} */ | ||
1347 | var source = sourceAndExtractedTags.source; | ||
1348 | job.source = source; | ||
1349 | job.basePos = 0; | ||
1350 | |||
1351 | /** Even entries are positions in source in ascending order. Odd entries | ||
1352 | * are tags that were extracted at that position. | ||
1353 | * @type {Array.<number|string>} | ||
1354 | */ | ||
1355 | job.extractedTags = sourceAndExtractedTags.tags; | ||
1356 | |||
1357 | // Apply the appropriate language handler | ||
1358 | langHandlerForExtension(opt_langExtension, source)(job); | ||
1359 | // Integrate the decorations and tags back into the source code to produce | ||
1360 | // a decorated html string which is left in job.prettyPrintedHtml. | ||
1361 | recombineTagsAndDecorations(job); | ||
1362 | } catch (e) { | ||
1363 | if ('console' in window) { | ||
1364 | console['log'](e && e['stack'] ? e['stack'] : e); | ||
1365 | } | ||
1366 | } | ||
1367 | } | ||
1368 | |||
1369 | function prettyPrintOne(sourceCodeHtml, opt_langExtension) { | ||
1370 | var job = { | ||
1371 | sourceCodeHtml: sourceCodeHtml, | ||
1372 | langExtension: opt_langExtension | ||
1373 | }; | ||
1374 | applyDecorator(job); | ||
1375 | return job.prettyPrintedHtml; | ||
1376 | } | ||
1377 | |||
1378 | function prettyPrint(opt_whenDone) { | ||
1379 | function byTagName(tn) { return document.getElementsByTagName(tn); } | ||
1380 | // fetch a list of nodes to rewrite | ||
1381 | var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')]; | ||
1382 | var elements = []; | ||
1383 | for (var i = 0; i < codeSegments.length; ++i) { | ||
1384 | for (var j = 0, n = codeSegments[i].length; j < n; ++j) { | ||
1385 | elements.push(codeSegments[i][j]); | ||
1386 | } | ||
1387 | } | ||
1388 | codeSegments = null; | ||
1389 | |||
1390 | var clock = Date; | ||
1391 | if (!clock['now']) { | ||
1392 | clock = { 'now': function () { return (new Date).getTime(); } }; | ||
1393 | } | ||
1394 | |||
1395 | // The loop is broken into a series of continuations to make sure that we | ||
1396 | // don't make the browser unresponsive when rewriting a large page. | ||
1397 | var k = 0; | ||
1398 | var prettyPrintingJob; | ||
1399 | |||
1400 | function doWork() { | ||
1401 | var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ? | ||
1402 | clock.now() + 250 /* ms */ : | ||
1403 | Infinity); | ||
1404 | for (; k < elements.length && clock.now() < endTime; k++) { | ||
1405 | var cs = elements[k]; | ||
1406 | // [JACOCO] 'prettyprint' -> 'source' | ||
1407 | if (cs.className && cs.className.indexOf('source') >= 0) { | ||
1408 | // If the classes includes a language extensions, use it. | ||
1409 | // Language extensions can be specified like | ||
1410 | // <pre class="prettyprint lang-cpp"> | ||
1411 | // the language extension "cpp" is used to find a language handler as | ||
1412 | // passed to PR_registerLangHandler. | ||
1413 | var langExtension = cs.className.match(/\blang-(\w+)\b/); | ||
1414 | if (langExtension) { langExtension = langExtension[1]; } | ||
1415 | |||
1416 | // make sure this is not nested in an already prettified element | ||
1417 | var nested = false; | ||
1418 | for (var p = cs.parentNode; p; p = p.parentNode) { | ||
1419 | if ((p.tagName === 'pre' || p.tagName === 'code' || | ||
1420 | p.tagName === 'xmp') && | ||
1421 | // [JACOCO] 'prettyprint' -> 'source' | ||
1422 | p.className && p.className.indexOf('source') >= 0) { | ||
1423 | nested = true; | ||
1424 | break; | ||
1425 | } | ||
1426 | } | ||
1427 | if (!nested) { | ||
1428 | // fetch the content as a snippet of properly escaped HTML. | ||
1429 | // Firefox adds newlines at the end. | ||
1430 | var content = getInnerHtml(cs); | ||
1431 | content = content.replace(/(?:\r\n?|\n)$/, ''); | ||
1432 | |||
1433 | // do the pretty printing | ||
1434 | prettyPrintingJob = { | ||
1435 | sourceCodeHtml: content, | ||
1436 | langExtension: langExtension, | ||
1437 | sourceNode: cs | ||
1438 | }; | ||
1439 | applyDecorator(prettyPrintingJob); | ||
1440 | replaceWithPrettyPrintedHtml(); | ||
1441 | } | ||
1442 | } | ||
1443 | } | ||
1444 | if (k < elements.length) { | ||
1445 | // finish up in a continuation | ||
1446 | setTimeout(doWork, 250); | ||
1447 | } else if (opt_whenDone) { | ||
1448 | opt_whenDone(); | ||
1449 | } | ||
1450 | } | ||
1451 | |||
1452 | function replaceWithPrettyPrintedHtml() { | ||
1453 | var newContent = prettyPrintingJob.prettyPrintedHtml; | ||
1454 | if (!newContent) { return; } | ||
1455 | var cs = prettyPrintingJob.sourceNode; | ||
1456 | |||
1457 | // push the prettified html back into the tag. | ||
1458 | if (!isRawContent(cs)) { | ||
1459 | // just replace the old html with the new | ||
1460 | cs.innerHTML = newContent; | ||
1461 | } else { | ||
1462 | // we need to change the tag to a <pre> since <xmp>s do not allow | ||
1463 | // embedded tags such as the span tags used to attach styles to | ||
1464 | // sections of source code. | ||
1465 | var pre = document.createElement('PRE'); | ||
1466 | for (var i = 0; i < cs.attributes.length; ++i) { | ||
1467 | var a = cs.attributes[i]; | ||
1468 | if (a.specified) { | ||
1469 | var aname = a.name.toLowerCase(); | ||
1470 | if (aname === 'class') { | ||
1471 | pre.className = a.value; // For IE 6 | ||
1472 | } else { | ||
1473 | pre.setAttribute(a.name, a.value); | ||
1474 | } | ||
1475 | } | ||
1476 | } | ||
1477 | pre.innerHTML = newContent; | ||
1478 | |||
1479 | // remove the old | ||
1480 | cs.parentNode.replaceChild(pre, cs); | ||
1481 | cs = pre; | ||
1482 | } | ||
1483 | } | ||
1484 | |||
1485 | doWork(); | ||
1486 | } | ||
1487 | |||
1488 | window['PR_normalizedHtml'] = normalizedHtml; | ||
1489 | window['prettyPrintOne'] = prettyPrintOne; | ||
1490 | window['prettyPrint'] = prettyPrint; | ||
1491 | window['PR'] = { | ||
1492 | 'combinePrefixPatterns': combinePrefixPatterns, | ||
1493 | 'createSimpleLexer': createSimpleLexer, | ||
1494 | 'registerLangHandler': registerLangHandler, | ||
1495 | 'sourceDecorator': sourceDecorator, | ||
1496 | 'PR_ATTRIB_NAME': PR_ATTRIB_NAME, | ||
1497 | 'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE, | ||
1498 | 'PR_COMMENT': PR_COMMENT, | ||
1499 | 'PR_DECLARATION': PR_DECLARATION, | ||
1500 | 'PR_KEYWORD': PR_KEYWORD, | ||
1501 | 'PR_LITERAL': PR_LITERAL, | ||
1502 | 'PR_NOCODE': PR_NOCODE, | ||
1503 | 'PR_PLAIN': PR_PLAIN, | ||
1504 | 'PR_PUNCTUATION': PR_PUNCTUATION, | ||
1505 | 'PR_SOURCE': PR_SOURCE, | ||
1506 | 'PR_STRING': PR_STRING, | ||
1507 | 'PR_TAG': PR_TAG, | ||
1508 | 'PR_TYPE': PR_TYPE | ||
1509 | }; | ||
1510 | })(); |