prettify.js 64 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712
  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. * @fileoverview
  16. * some functions for browser-side pretty printing of code contained in html.
  17. *
  18. * <p>
  19. * For a fairly comprehensive set of languages see the
  20. * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
  21. * file that came with this source. At a minimum, the lexer should work on a
  22. * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
  23. * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk
  24. * and a subset of Perl, but, because of commenting conventions, doesn't work on
  25. * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
  26. * <p>
  27. * Usage: <ol>
  28. * <li> include this source file in an html page via
  29. * {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
  30. * <li> define style rules. See the example page for examples.
  31. * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
  32. * {@code class=prettyprint.}
  33. * You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
  34. * printer needs to do more substantial DOM manipulations to support that, so
  35. * some css styles may not be preserved.
  36. * </ol>
  37. * That's it. I wanted to keep the API as simple as possible, so there's no
  38. * need to specify which language the code is in, but if you wish, you can add
  39. * another class to the {@code <pre>} or {@code <code>} element to specify the
  40. * language, as in {@code <pre class="prettyprint lang-java">}. Any class that
  41. * starts with "lang-" followed by a file extension, specifies the file type.
  42. * See the "lang-*.js" files in this directory for code that implements
  43. * per-language file handlers.
  44. * <p>
  45. * Change log:<br>
  46. * cbeust, 2006/08/22
  47. * <blockquote>
  48. * Java annotations (start with "@") are now captured as literals ("lit")
  49. * </blockquote>
  50. * @requires console
  51. */
  52. // JSLint declarations
  53. /*global console, document, navigator, setTimeout, window, define */
  54. /** @define {boolean} */
  55. var IN_GLOBAL_SCOPE = true;
  56. /**
  57. * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
  58. * UI events.
  59. * If set to {@code false}, {@code prettyPrint()} is synchronous.
  60. */
  61. window['PR_SHOULD_USE_CONTINUATION'] = true;
  62. /**
  63. * Pretty print a chunk of code.
  64. * @param {string} sourceCodeHtml The HTML to pretty print.
  65. * @param {string} opt_langExtension The language name to use.
  66. * Typically, a filename extension like 'cpp' or 'java'.
  67. * @param {number|boolean} opt_numberLines True to number lines,
  68. * or the 1-indexed number of the first line in sourceCodeHtml.
  69. * @return {string} code as html, but prettier
  70. */
  71. var prettyPrintOne;
  72. /**
  73. * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
  74. * {@code class=prettyprint} and prettify them.
  75. *
  76. * @param {Function} opt_whenDone called when prettifying is done.
  77. * @param {HTMLElement|HTMLDocument} opt_root an element or document
  78. * containing all the elements to pretty print.
  79. * Defaults to {@code document.body}.
  80. */
  81. var prettyPrint;
  82. (function () {
  83. var win = window;
  84. // Keyword lists for various languages.
  85. // We use things that coerce to strings to make them compact when minified
  86. // and to defeat aggressive optimizers that fold large string constants.
  87. var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
  88. var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," +
  89. "double,enum,extern,float,goto,inline,int,long,register,short,signed," +
  90. "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
  91. var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
  92. "new,operator,private,protected,public,this,throw,true,try,typeof"];
  93. var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
  94. "concept,concept_map,const_cast,constexpr,decltype,delegate," +
  95. "dynamic_cast,explicit,export,friend,generic,late_check," +
  96. "mutable,namespace,nullptr,property,reinterpret_cast,static_assert," +
  97. "static_cast,template,typeid,typename,using,virtual,where"];
  98. var JAVA_KEYWORDS = [COMMON_KEYWORDS,
  99. "abstract,assert,boolean,byte,extends,final,finally,implements,import," +
  100. "instanceof,interface,null,native,package,strictfp,super,synchronized," +
  101. "throws,transient"];
  102. var CSHARP_KEYWORDS = [COMMON_KEYWORDS,
  103. "abstract,as,base,bool,by,byte,checked,decimal,delegate,descending," +
  104. "dynamic,event,finally,fixed,foreach,from,group,implicit,in,interface," +
  105. "internal,into,is,let,lock,null,object,out,override,orderby,params," +
  106. "partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong," +
  107. "unchecked,unsafe,ushort,var,virtual,where"];
  108. var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
  109. "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
  110. "throw,true,try,unless,until,when,while,yes";
  111. var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
  112. "debugger,eval,export,function,get,null,set,undefined,var,with," +
  113. "Infinity,NaN"];
  114. var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
  115. "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
  116. "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
  117. var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
  118. "elif,except,exec,finally,from,global,import,in,is,lambda," +
  119. "nonlocal,not,or,pass,print,raise,try,with,yield," +
  120. "False,True,None"];
  121. var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
  122. "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
  123. "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
  124. "BEGIN,END"];
  125. var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "as,assert,const,copy,drop," +
  126. "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv," +
  127. "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"];
  128. var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
  129. "function,in,local,set,then,until"];
  130. var ALL_KEYWORDS = [
  131. CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS,
  132. PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
  133. var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
  134. // token style names. correspond to css classes
  135. /**
  136. * token style for a string literal
  137. * @const
  138. */
  139. var PR_STRING = 'str';
  140. /**
  141. * token style for a keyword
  142. * @const
  143. */
  144. var PR_KEYWORD = 'kwd';
  145. /**
  146. * token style for a comment
  147. * @const
  148. */
  149. var PR_COMMENT = 'com';
  150. /**
  151. * token style for a type
  152. * @const
  153. */
  154. var PR_TYPE = 'typ';
  155. /**
  156. * token style for a literal value. e.g. 1, null, true.
  157. * @const
  158. */
  159. var PR_LITERAL = 'lit';
  160. /**
  161. * token style for a punctuation string.
  162. * @const
  163. */
  164. var PR_PUNCTUATION = 'pun';
  165. /**
  166. * token style for plain text.
  167. * @const
  168. */
  169. var PR_PLAIN = 'pln';
  170. /**
  171. * token style for an sgml tag.
  172. * @const
  173. */
  174. var PR_TAG = 'tag';
  175. /**
  176. * token style for a markup declaration such as a DOCTYPE.
  177. * @const
  178. */
  179. var PR_DECLARATION = 'dec';
  180. /**
  181. * token style for embedded source.
  182. * @const
  183. */
  184. var PR_SOURCE = 'src';
  185. /**
  186. * token style for an sgml attribute name.
  187. * @const
  188. */
  189. var PR_ATTRIB_NAME = 'atn';
  190. /**
  191. * token style for an sgml attribute value.
  192. * @const
  193. */
  194. var PR_ATTRIB_VALUE = 'atv';
  195. /**
  196. * A class that indicates a section of markup that is not code, e.g. to allow
  197. * embedding of line numbers within code listings.
  198. * @const
  199. */
  200. var PR_NOCODE = 'nocode';
  201. /**
  202. * A set of tokens that can precede a regular expression literal in
  203. * javascript
  204. * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
  205. * has the full list, but I've removed ones that might be problematic when
  206. * seen in languages that don't support regular expression literals.
  207. *
  208. * <p>Specifically, I've removed any keywords that can't precede a regexp
  209. * literal in a syntactically legal javascript program, and I've removed the
  210. * "in" keyword since it's not a keyword in many languages, and might be used
  211. * as a count of inches.
  212. *
  213. * <p>The link above does not accurately describe EcmaScript rules since
  214. * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
  215. * very well in practice.
  216. *
  217. * @private
  218. * @const
  219. */
  220. var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
  221. // CAVEAT: this does not properly handle the case where a regular
  222. // expression immediately follows another since a regular expression may
  223. // have flags for case-sensitivity and the like. Having regexp tokens
  224. // adjacent is not valid in any language I'm aware of, so I'm punting.
  225. // TODO: maybe style special characters inside a regexp as punctuation.
  226. /**
  227. * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
  228. * matches the union of the sets of strings matched by the input RegExp.
  229. * Since it matches globally, if the input strings have a start-of-input
  230. * anchor (/^.../), it is ignored for the purposes of unioning.
  231. * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
  232. * @return {RegExp} a global regex.
  233. */
  234. function combinePrefixPatterns(regexs) {
  235. var capturedGroupIndex = 0;
  236. var needToFoldCase = false;
  237. var ignoreCase = false;
  238. for (var i = 0, n = regexs.length; i < n; ++i) {
  239. const regex = regexs[i];
  240. if (regex.ignoreCase) {
  241. ignoreCase = true;
  242. } else if (/[a-z]/i.test(regex.source.replace(
  243. /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
  244. needToFoldCase = true;
  245. ignoreCase = false;
  246. break;
  247. }
  248. }
  249. var escapeCharToCodeUnit = {
  250. 'b': 8,
  251. 't': 9,
  252. 'n': 0xa,
  253. 'v': 0xb,
  254. 'f': 0xc,
  255. 'r': 0xd
  256. };
  257. function decodeEscape(charsetPart) {
  258. var cc0 = charsetPart.charCodeAt(0);
  259. if (cc0 !== 92 /* \\ */) {
  260. return cc0;
  261. }
  262. var c1 = charsetPart.charAt(1);
  263. cc0 = escapeCharToCodeUnit[c1];
  264. if (cc0) {
  265. return cc0;
  266. } else if ('0' <= c1 && c1 <= '7') {
  267. return parseInt(charsetPart.substring(1), 8);
  268. } else if (c1 === 'u' || c1 === 'x') {
  269. return parseInt(charsetPart.substring(2), 16);
  270. } else {
  271. return charsetPart.charCodeAt(1);
  272. }
  273. }
  274. function encodeEscape(charCode) {
  275. if (charCode < 0x20) {
  276. return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
  277. }
  278. var ch = String.fromCharCode(charCode);
  279. return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
  280. ? "\\" + ch : ch;
  281. }
  282. function caseFoldCharset(charSet) {
  283. var charsetParts = charSet.substring(1, charSet.length - 1).match(
  284. new RegExp(
  285. '\\\\u[0-9A-Fa-f]{4}'
  286. + '|\\\\x[0-9A-Fa-f]{2}'
  287. + '|\\\\[0-3][0-7]{0,2}'
  288. + '|\\\\[0-7]{1,2}'
  289. + '|\\\\[\\s\\S]'
  290. + '|-'
  291. + '|[^-\\\\]',
  292. 'g'));
  293. var ranges = [];
  294. var inverse = charsetParts[0] === '^';
  295. var out = ['['];
  296. if (inverse) { out.push('^'); }
  297. for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
  298. var p = charsetParts[i];
  299. if (/\\[bdsw]/i.test(p)) { // Don't muck with named groups.
  300. out.push(p);
  301. } else {
  302. var start = decodeEscape(p);
  303. var end;
  304. if (i + 2 < n && '-' === charsetParts[i + 1]) {
  305. end = decodeEscape(charsetParts[i + 2]);
  306. i += 2;
  307. } else {
  308. end = start;
  309. }
  310. ranges.push([start, end]);
  311. // If the range might intersect letters, then expand it.
  312. // This case handling is too simplistic.
  313. // It does not deal with non-latin case folding.
  314. // It works for latin source code identifiers though.
  315. if (!(end < 65 || start > 122)) {
  316. if (!(end < 65 || start > 90)) {
  317. ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
  318. }
  319. if (!(end < 97 || start > 122)) {
  320. ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
  321. }
  322. }
  323. }
  324. }
  325. // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
  326. // -> [[1, 12], [14, 14], [16, 17]]
  327. ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); });
  328. var consolidatedRanges = [];
  329. var lastRange = [];
  330. for (var i = 0; i < ranges.length; ++i) {
  331. const range = ranges[i];
  332. if (range[0] <= lastRange[1] + 1) {
  333. lastRange[1] = Math.max(lastRange[1], range[1]);
  334. } else {
  335. consolidatedRanges.push(lastRange = range);
  336. }
  337. }
  338. for (var i = 0; i < consolidatedRanges.length; ++i) {
  339. const range = consolidatedRanges[i];
  340. out.push(encodeEscape(range[0]));
  341. if (range[1] > range[0]) {
  342. if (range[1] + 1 > range[0]) { out.push('-'); }
  343. out.push(encodeEscape(range[1]));
  344. }
  345. }
  346. out.push(']');
  347. return out.join('');
  348. }
  349. function allowAnywhereFoldCaseAndRenumberGroups(regex) {
  350. // Split into character sets, escape sequences, punctuation strings
  351. // like ('(', '(?:', ')', '^'), and runs of characters that do not
  352. // include any of the above.
  353. var parts = regex.source.match(
  354. new RegExp(
  355. '(?:'
  356. + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set
  357. + '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape
  358. + '|\\\\x[A-Fa-f0-9]{2}' // a hex escape
  359. + '|\\\\[0-9]+' // a back-reference or octal escape
  360. + '|\\\\[^ux0-9]' // other escape sequence
  361. + '|\\(\\?[:!=]' // start of a non-capturing group
  362. + '|[\\(\\)\\^]' // start/end of a group, or line start
  363. + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters
  364. + ')',
  365. 'g'));
  366. var n = parts.length;
  367. // Maps captured group numbers to the number they will occupy in
  368. // the output or to -1 if that has not been determined, or to
  369. // undefined if they need not be capturing in the output.
  370. var capturedGroups = [];
  371. // Walk over and identify back references to build the capturedGroups
  372. // mapping.
  373. for (let i = 0, groupIndex = 0; i < n; ++i) {
  374. var p = parts[i];
  375. if (p === '(') {
  376. // groups are 1-indexed, so max group index is count of '('
  377. ++groupIndex;
  378. } else if ('\\' === p.charAt(0)) {
  379. const decimalValue = +p.substring(1);
  380. if (decimalValue) {
  381. if (decimalValue <= groupIndex) {
  382. capturedGroups[decimalValue] = -1;
  383. } else {
  384. // Replace with an unambiguous escape sequence so that
  385. // an octal escape sequence does not turn into a backreference
  386. // to a capturing group from an earlier regex.
  387. parts[i] = encodeEscape(decimalValue);
  388. }
  389. }
  390. }
  391. }
  392. // Renumber groups and reduce capturing groups to non-capturing groups
  393. // where possible.
  394. for (var i = 1; i < capturedGroups.length; ++i) {
  395. if (-1 === capturedGroups[i]) {
  396. capturedGroups[i] = ++capturedGroupIndex;
  397. }
  398. }
  399. for (let i = 0, groupIndex = 0; i < n; ++i) {
  400. var p = parts[i];
  401. if (p === '(') {
  402. ++groupIndex;
  403. if (!capturedGroups[groupIndex]) {
  404. parts[i] = '(?:';
  405. }
  406. } else if ('\\' === p.charAt(0)) {
  407. const decimalValue = +p.substring(1);
  408. if (decimalValue && decimalValue <= groupIndex) {
  409. parts[i] = '\\' + capturedGroups[decimalValue];
  410. }
  411. }
  412. }
  413. // Remove any prefix anchors so that the output will match anywhere.
  414. // ^^ really does mean an anchored match though.
  415. for (var i = 0; i < n; ++i) {
  416. if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
  417. }
  418. // Expand letters to groups to handle mixing of case-sensitive and
  419. // case-insensitive patterns if necessary.
  420. if (regex.ignoreCase && needToFoldCase) {
  421. for (var i = 0; i < n; ++i) {
  422. var p = parts[i];
  423. var ch0 = p.charAt(0);
  424. if (p.length >= 2 && ch0 === '[') {
  425. parts[i] = caseFoldCharset(p);
  426. } else if (ch0 !== '\\') {
  427. // TODO: handle letters in numeric escapes.
  428. parts[i] = p.replace(
  429. /[a-zA-Z]/g,
  430. function (ch) {
  431. var cc = ch.charCodeAt(0);
  432. return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
  433. });
  434. }
  435. }
  436. }
  437. return parts.join('');
  438. }
  439. var rewritten = [];
  440. for (var i = 0, n = regexs.length; i < n; ++i) {
  441. const regex = regexs[i];
  442. if (regex.global || regex.multiline) { throw new Error('' + regex); }
  443. rewritten.push(
  444. '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
  445. }
  446. return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
  447. }
  448. /**
  449. * Split markup into a string of source code and an array mapping ranges in
  450. * that string to the text nodes in which they appear.
  451. *
  452. * <p>
  453. * The HTML DOM structure:</p>
  454. * <pre>
  455. * (Element "p"
  456. * (Element "b"
  457. * (Text "print ")) ; #1
  458. * (Text "'Hello '") ; #2
  459. * (Element "br") ; #3
  460. * (Text " + 'World';")) ; #4
  461. * </pre>
  462. * <p>
  463. * corresponds to the HTML
  464. * {@code <p><b>print </b>'Hello '<br> + 'World';</p>}.</p>
  465. *
  466. * <p>
  467. * It will produce the output:</p>
  468. * <pre>
  469. * {
  470. * sourceCode: "print 'Hello '\n + 'World';",
  471. * // 1 2
  472. * // 012345678901234 5678901234567
  473. * spans: [0, #1, 6, #2, 14, #3, 15, #4]
  474. * }
  475. * </pre>
  476. * <p>
  477. * where #1 is a reference to the {@code "print "} text node above, and so
  478. * on for the other text nodes.
  479. * </p>
  480. *
  481. * <p>
  482. * The {@code} spans array is an array of pairs. Even elements are the start
  483. * indices of substrings, and odd elements are the text nodes (or BR elements)
  484. * that contain the text for those substrings.
  485. * Substrings continue until the next index or the end of the source.
  486. * </p>
  487. *
  488. * @param {Node} node an HTML DOM subtree containing source-code.
  489. * @param {boolean} isPreformatted true if white-space in text nodes should
  490. * be considered significant.
  491. * @return {Object} source code and the text nodes in which they occur.
  492. */
  493. function extractSourceSpans(node, isPreformatted) {
  494. var nocode = /(?:^|\s)nocode(?:\s|$)/;
  495. var chunks = [];
  496. var length = 0;
  497. var spans = [];
  498. var k = 0;
  499. function walk(node) {
  500. var type = node.nodeType;
  501. if (type == 1) { // Element
  502. if (nocode.test(node.className)) { return; }
  503. for (var child = node.firstChild; child; child = child.nextSibling) {
  504. walk(child);
  505. }
  506. var nodeName = node.nodeName.toLowerCase();
  507. if ('br' === nodeName || 'li' === nodeName) {
  508. chunks[k] = '\n';
  509. spans[k << 1] = length++;
  510. spans[(k++ << 1) | 1] = node;
  511. }
  512. } else if (type == 3 || type == 4) { // Text
  513. var text = node.nodeValue;
  514. if (text.length) {
  515. if (!isPreformatted) {
  516. text = text.replace(/[ \t\r\n]+/g, ' ');
  517. } else {
  518. text = text.replace(/\r\n?/g, '\n'); // Normalize newlines.
  519. }
  520. // TODO: handle tabs here?
  521. chunks[k] = text;
  522. spans[k << 1] = length;
  523. length += text.length;
  524. spans[(k++ << 1) | 1] = node;
  525. }
  526. }
  527. }
  528. walk(node);
  529. return {
  530. sourceCode: chunks.join('').replace(/\n$/, ''),
  531. spans: spans
  532. };
  533. }
  534. /**
  535. * Apply the given language handler to sourceCode and add the resulting
  536. * decorations to out.
  537. * @param {number} basePos the index of sourceCode within the chunk of source
  538. * whose decorations are already present on out.
  539. */
  540. function appendDecorations(basePos, sourceCode, langHandler, out) {
  541. if (!sourceCode) { return; }
  542. var job = {
  543. sourceCode: sourceCode,
  544. basePos: basePos
  545. };
  546. langHandler(job);
  547. out.push.apply(out, job.decorations);
  548. }
  549. var notWs = /\S/;
  550. /**
  551. * Given an element, if it contains only one child element and any text nodes
  552. * it contains contain only space characters, return the sole child element.
  553. * Otherwise returns undefined.
  554. * <p>
  555. * This is meant to return the CODE element in {@code <pre><code ...>} when
  556. * there is a single child element that contains all the non-space textual
  557. * content, but not to return anything where there are multiple child elements
  558. * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
  559. * is textual content.
  560. */
  561. function childContentWrapper(element) {
  562. var wrapper = undefined;
  563. for (var c = element.firstChild; c; c = c.nextSibling) {
  564. var type = c.nodeType;
  565. wrapper = (type === 1) // Element Node
  566. ? (wrapper ? element : c)
  567. : (type === 3) // Text Node
  568. ? (notWs.test(c.nodeValue) ? element : wrapper)
  569. : wrapper;
  570. }
  571. return wrapper === element ? undefined : wrapper;
  572. }
  573. /** Given triples of [style, pattern, context] returns a lexing function,
  574. * The lexing function interprets the patterns to find token boundaries and
  575. * returns a decoration list of the form
  576. * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
  577. * where index_n is an index into the sourceCode, and style_n is a style
  578. * constant like PR_PLAIN. index_n-1 <= index_n, and style_n-1 applies to
  579. * all characters in sourceCode[index_n-1:index_n].
  580. *
  581. * The stylePatterns is a list whose elements have the form
  582. * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
  583. *
  584. * Style is a style constant like PR_PLAIN, or can be a string of the
  585. * form 'lang-FOO', where FOO is a language extension describing the
  586. * language of the portion of the token in $1 after pattern executes.
  587. * E.g., if style is 'lang-lisp', and group 1 contains the text
  588. * '(hello (world))', then that portion of the token will be passed to the
  589. * registered lisp handler for formatting.
  590. * The text before and after group 1 will be restyled using this decorator
  591. * so decorators should take care that this doesn't result in infinite
  592. * recursion. For example, the HTML lexer rule for SCRIPT elements looks
  593. * something like ['lang-js', /<[s]cript>(.+?)<\/script>/]. This may match
  594. * '<script>foo()<\/script>', which would cause the current decorator to
  595. * be called with '<script>' which would not match the same rule since
  596. * group 1 must not be empty, so it would be instead styled as PR_TAG by
  597. * the generic tag rule. The handler registered for the 'js' extension would
  598. * then be called with 'foo()', and finally, the current decorator would
  599. * be called with '<\/script>' which would not match the original rule and
  600. * so the generic tag rule would identify it as a tag.
  601. *
  602. * Pattern must only match prefixes, and if it matches a prefix, then that
  603. * match is considered a token with the same style.
  604. *
  605. * Context is applied to the last non-whitespace, non-comment token
  606. * recognized.
  607. *
  608. * Shortcut is an optional string of characters, any of which, if the first
  609. * character, gurantee that this pattern and only this pattern matches.
  610. *
  611. * @param {Array} shortcutStylePatterns patterns that always start with
  612. * a known character. Must have a shortcut string.
  613. * @param {Array} fallthroughStylePatterns patterns that will be tried in
  614. * order if the shortcut ones fail. May have shortcuts.
  615. *
  616. * @return {function (Object)} a
  617. * function that takes source code and returns a list of decorations.
  618. */
  619. function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
  620. var shortcuts = {};
  621. var tokenizer;
  622. (function () {
  623. var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
  624. var allRegexs = [];
  625. var regexKeys = {};
  626. for (var i = 0, n = allPatterns.length; i < n; ++i) {
  627. var patternParts = allPatterns[i];
  628. var shortcutChars = patternParts[3];
  629. if (shortcutChars) {
  630. for (var c = shortcutChars.length; --c >= 0;) {
  631. shortcuts[shortcutChars.charAt(c)] = patternParts;
  632. }
  633. }
  634. var regex = patternParts[1];
  635. var k = '' + regex;
  636. if (!regexKeys.hasOwnProperty(k)) {
  637. allRegexs.push(regex);
  638. regexKeys[k] = null;
  639. }
  640. }
  641. allRegexs.push(/[\0-\uffff]/);
  642. tokenizer = combinePrefixPatterns(allRegexs);
  643. })();
  644. var nPatterns = fallthroughStylePatterns.length;
  645. /**
  646. * Lexes job.sourceCode and produces an output array job.decorations of
  647. * style classes preceded by the position at which they start in
  648. * job.sourceCode in order.
  649. *
  650. * @param {Object} job an object like <pre>{
  651. * sourceCode: {string} sourceText plain text,
  652. * basePos: {int} position of job.sourceCode in the larger chunk of
  653. * sourceCode.
  654. * }</pre>
  655. */
  656. var decorate = function (job) {
  657. var sourceCode = job.sourceCode, basePos = job.basePos;
  658. /** Even entries are positions in source in ascending order. Odd enties
  659. * are style markers (e.g., PR_COMMENT) that run from that position until
  660. * the end.
  661. * @type {Array.<number|string>}
  662. */
  663. var decorations = [basePos, PR_PLAIN];
  664. var pos = 0; // index into sourceCode
  665. var tokens = sourceCode.match(tokenizer) || [];
  666. var styleCache = {};
  667. for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
  668. var token = tokens[ti];
  669. var style = styleCache[token];
  670. var match = void 0;
  671. var isEmbedded;
  672. if (typeof style === 'string') {
  673. isEmbedded = false;
  674. } else {
  675. var patternParts = shortcuts[token.charAt(0)];
  676. if (patternParts) {
  677. match = token.match(patternParts[1]);
  678. style = patternParts[0];
  679. } else {
  680. for (var i = 0; i < nPatterns; ++i) {
  681. patternParts = fallthroughStylePatterns[i];
  682. match = token.match(patternParts[1]);
  683. if (match) {
  684. style = patternParts[0];
  685. break;
  686. }
  687. }
  688. if (!match) { // make sure that we make progress
  689. style = PR_PLAIN;
  690. }
  691. }
  692. isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
  693. if (isEmbedded && !(match && typeof match[1] === 'string')) {
  694. isEmbedded = false;
  695. style = PR_SOURCE;
  696. }
  697. if (!isEmbedded) { styleCache[token] = style; }
  698. }
  699. var tokenStart = pos;
  700. pos += token.length;
  701. if (!isEmbedded) {
  702. decorations.push(basePos + tokenStart, style);
  703. } else { // Treat group 1 as an embedded block of source code.
  704. var embeddedSource = match[1];
  705. var embeddedSourceStart = token.indexOf(embeddedSource);
  706. var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
  707. if (match[2]) {
  708. // If embeddedSource can be blank, then it would match at the
  709. // beginning which would cause us to infinitely recurse on the
  710. // entire token, so we catch the right context in match[2].
  711. embeddedSourceEnd = token.length - match[2].length;
  712. embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
  713. }
  714. var lang = style.substring(5);
  715. // Decorate the left of the embedded source
  716. appendDecorations(
  717. basePos + tokenStart,
  718. token.substring(0, embeddedSourceStart),
  719. decorate, decorations);
  720. // Decorate the embedded source
  721. appendDecorations(
  722. basePos + tokenStart + embeddedSourceStart,
  723. embeddedSource,
  724. langHandlerForExtension(lang, embeddedSource),
  725. decorations);
  726. // Decorate the right of the embedded section
  727. appendDecorations(
  728. basePos + tokenStart + embeddedSourceEnd,
  729. token.substring(embeddedSourceEnd),
  730. decorate, decorations);
  731. }
  732. }
  733. job.decorations = decorations;
  734. };
  735. return decorate;
  736. }
  737. /** returns a function that produces a list of decorations from source text.
  738. *
  739. * This code treats ", ', and ` as string delimiters, and \ as a string
  740. * escape. It does not recognize perl's qq() style strings.
  741. * It has no special handling for double delimiter escapes as in basic, or
  742. * the tripled delimiters used in python, but should work on those regardless
  743. * although in those cases a single string literal may be broken up into
  744. * multiple adjacent string literals.
  745. *
  746. * It recognizes C, C++, and shell style comments.
  747. *
  748. * @param {Object} options a set of optional parameters.
  749. * @return {function (Object)} a function that examines the source code
  750. * in the input job and builds the decoration list.
  751. */
  752. function sourceDecorator(options) {
  753. var shortcutStylePatterns = [], fallthroughStylePatterns = [];
  754. if (options['tripleQuotedStrings']) {
  755. // '''multi-line-string''', 'single-line-string', and double-quoted
  756. shortcutStylePatterns.push(
  757. [PR_STRING, /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
  758. null, '\'"']);
  759. } else if (options['multiLineStrings']) {
  760. // 'multi-line-string', "multi-line-string"
  761. shortcutStylePatterns.push(
  762. [PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
  763. null, '\'"`']);
  764. } else {
  765. // 'single-line-string', "single-line-string"
  766. shortcutStylePatterns.push(
  767. [PR_STRING,
  768. /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
  769. null, '"\'']);
  770. }
  771. if (options['verbatimStrings']) {
  772. // verbatim-string-literal production from the C# grammar. See issue 93.
  773. fallthroughStylePatterns.push(
  774. [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
  775. }
  776. var hc = options['hashComments'];
  777. if (hc) {
  778. if (options['cStyleComments']) {
  779. if (hc > 1) { // multiline hash comments
  780. shortcutStylePatterns.push(
  781. [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
  782. } else {
  783. // Stop C preprocessor declarations at an unclosed open comment
  784. shortcutStylePatterns.push(
  785. [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
  786. null, '#']);
  787. }
  788. // #include <stdio.h>
  789. fallthroughStylePatterns.push(
  790. [PR_STRING,
  791. /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
  792. null]);
  793. } else {
  794. shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
  795. }
  796. }
  797. if (options['cStyleComments']) {
  798. fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
  799. fallthroughStylePatterns.push(
  800. [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
  801. }
  802. var regexLiterals = options['regexLiterals'];
  803. if (regexLiterals) {
  804. /**
  805. * @const
  806. */
  807. var regexExcls = regexLiterals > 1
  808. ? '' // Multiline regex literals
  809. : '\n\r';
  810. /**
  811. * @const
  812. */
  813. var regexAny = regexExcls ? '.' : '[\\S\\s]';
  814. /**
  815. * @const
  816. */
  817. var REGEX_LITERAL = (
  818. // A regular expression literal starts with a slash that is
  819. // not followed by * or / so that it is not confused with
  820. // comments.
  821. '/(?=[^/*' + regexExcls + '])'
  822. // and then contains any number of raw characters,
  823. + '(?:[^/\\x5B\\x5C' + regexExcls + ']'
  824. // escape sequences (\x5C),
  825. + '|\\x5C' + regexAny
  826. // or non-nesting character sets (\x5B\x5D);
  827. + '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']'
  828. + '|\\x5C' + regexAny + ')*(?:\\x5D|$))+'
  829. // finally closed by a /.
  830. + '/');
  831. fallthroughStylePatterns.push(
  832. ['lang-regex',
  833. RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
  834. ]);
  835. }
  836. var types = options['types'];
  837. if (types) {
  838. fallthroughStylePatterns.push([PR_TYPE, types]);
  839. }
  840. var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
  841. if (keywords.length) {
  842. fallthroughStylePatterns.push(
  843. [PR_KEYWORD,
  844. new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
  845. null]);
  846. }
  847. shortcutStylePatterns.push([PR_PLAIN, /^\s+/, null, ' \r\n\t\xA0']);
  848. var punctuation =
  849. // The Bash man page says
  850. // A word is a sequence of characters considered as a single
  851. // unit by GRUB. Words are separated by metacharacters,
  852. // which are the following plus space, tab, and newline: { }
  853. // | & $ ; < >
  854. // ...
  855. // A word beginning with # causes that word and all remaining
  856. // characters on that line to be ignored.
  857. // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
  858. // comment but empirically
  859. // $ echo {#}
  860. // {#}
  861. // $ echo \$#
  862. // $#
  863. // $ echo }#
  864. // }#
  865. // so /(?:^|[|&;<>\s])/ is more appropriate.
  866. // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
  867. // suggests that this definition is compatible with a
  868. // default mode that tries to use a single token definition
  869. // to recognize both bash/python style comments and C
  870. // preprocessor directives.
  871. // This definition of punctuation does not include # in the list of
  872. // follow-on exclusions, so # will not be broken before if preceeded
  873. // by a punctuation character. We could try to exclude # after
  874. // [|&;<>] but that doesn't seem to cause many major problems.
  875. // If that does turn out to be a problem, we should change the below
  876. // when hc is truthy to include # in the run of punctuation characters
  877. // only when not followint [|&;<>].
  878. '^.[^\\s\\w.$@\'"`/\\\\]*';
  879. if (options['regexLiterals']) {
  880. punctuation += '(?!s*/)';
  881. }
  882. fallthroughStylePatterns.push(
  883. // TODO(mikesamuel): recognize non-latin letters and numerals in idents
  884. [PR_LITERAL, /^@[a-z_$][a-z_$@0-9]*/i, null],
  885. [PR_TYPE, /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
  886. [PR_PLAIN, /^[a-z_$][a-z_$@0-9]*/i, null],
  887. [PR_LITERAL,
  888. new RegExp(
  889. '^(?:'
  890. // A hex number
  891. + '0x[a-f0-9]+'
  892. // or an octal or decimal number,
  893. + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
  894. // possibly in scientific notation
  895. + '(?:e[+\\-]?\\d+)?'
  896. + ')'
  897. // with an optional modifier like UL for unsigned long
  898. + '[a-z]*', 'i'),
  899. null, '0123456789'],
  900. // Don't treat escaped quotes in bash as starting strings.
  901. // See issue 144.
  902. [PR_PLAIN, /^\\[\s\S]?/, null],
  903. [PR_PUNCTUATION, new RegExp(punctuation), null]);
  904. return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
  905. }
  906. var decorateSource = sourceDecorator({
  907. 'keywords': ALL_KEYWORDS,
  908. 'hashComments': true,
  909. 'cStyleComments': true,
  910. 'multiLineStrings': true,
  911. 'regexLiterals': true
  912. });
  913. /**
  914. * Given a DOM subtree, wraps it in a list, and puts each line into its own
  915. * list item.
  916. *
  917. * @param {Node} node modified in place. Its content is pulled into an
  918. * HTMLOListElement, and each line is moved into a separate list item.
  919. * This requires cloning elements, so the input might not have unique
  920. * IDs after numbering.
  921. * @param {boolean} isPreformatted true if white-space in text nodes should
  922. * be treated as significant.
  923. */
  924. function numberLines(node, opt_startLineNum, isPreformatted, opt_numberLines, opt_calloutModifiedLines) {
  925. var nocode = /(?:^|\s)nocode(?:\s|$)/;
  926. var lineBreak = /\r\n?|\n/;
  927. var prefixes = [
  928. { prefix: "*", className: "linemodified", },
  929. { prefix: "-", className: "linedeleted", },
  930. { prefix: "+", className: "lineadded", },
  931. ];
  932. opt_numberLines = (opt_numberLines == undefined) ? true : opt_numberLines;
  933. var startsWith = function(str, prefix) {
  934. return str.length >= prefix.length && str.substring(0, prefix.length) == prefix;
  935. };
  936. var document = node.ownerDocument;
  937. var li = document.createElement('li');
  938. while (node.firstChild) {
  939. li.appendChild(node.firstChild);
  940. }
  941. // An array of lines. We split below, so this is initialized to one
  942. // un-split line.
  943. var listItems = [li];
  944. function walk(node) {
  945. var type = node.nodeType;
  946. if (type == 1 && !nocode.test(node.className)) { // Element
  947. if ('br' === node.nodeName) {
  948. breakAfter(node);
  949. // Discard the <BR> since it is now flush against a </LI>.
  950. if (node.parentNode) {
  951. node.parentNode.removeChild(node);
  952. }
  953. } else {
  954. for (var child = node.firstChild; child; child = child.nextSibling) {
  955. walk(child);
  956. }
  957. }
  958. } else if ((type == 3 || type == 4) && isPreformatted) { // Text
  959. var text = node.nodeValue;
  960. var match = text.match(lineBreak);
  961. if (match) {
  962. var firstLine = text.substring(0, match.index);
  963. node.nodeValue = firstLine;
  964. var tail = text.substring(match.index + match[0].length);
  965. if (tail) {
  966. var parent = node.parentNode;
  967. parent.insertBefore(
  968. document.createTextNode(tail), node.nextSibling);
  969. }
  970. breakAfter(node);
  971. if (!firstLine) {
  972. // Don't leave blank text nodes in the DOM.
  973. node.parentNode.removeChild(node);
  974. }
  975. }
  976. }
  977. }
  978. // Split a line after the given node.
  979. function breakAfter(lineEndNode) {
  980. // If there's nothing to the right, then we can skip ending the line
  981. // here, and move root-wards since splitting just before an end-tag
  982. // would require us to create a bunch of empty copies.
  983. while (!lineEndNode.nextSibling) {
  984. lineEndNode = lineEndNode.parentNode;
  985. if (!lineEndNode) { return; }
  986. }
  987. function breakLeftOf(limit, copy) {
  988. // Clone shallowly if this node needs to be on both sides of the break.
  989. var rightSide = copy ? limit.cloneNode(false) : limit;
  990. var parent = limit.parentNode;
  991. if (parent) {
  992. // We clone the parent chain.
  993. // This helps us resurrect important styling elements that cross lines.
  994. // E.g. in <i>Foo<br>Bar</i>
  995. // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
  996. var parentClone = breakLeftOf(parent, 1);
  997. // Move the clone and everything to the right of the original
  998. // onto the cloned parent.
  999. var next = limit.nextSibling;
  1000. parentClone.appendChild(rightSide);
  1001. for (var sibling = next; sibling; sibling = next) {
  1002. next = sibling.nextSibling;
  1003. parentClone.appendChild(sibling);
  1004. }
  1005. }
  1006. return rightSide;
  1007. }
  1008. var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
  1009. // Walk the parent chain until we reach an unattached LI.
  1010. for (var parent;
  1011. // Check nodeType since IE invents document fragments.
  1012. (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
  1013. copiedListItem = parent;
  1014. }
  1015. // Put it on the list of lines for later processing.
  1016. listItems.push(copiedListItem);
  1017. }
  1018. // Split lines while there are lines left to split.
  1019. for (var i = 0; // Number of lines that have been split so far.
  1020. i < listItems.length; // length updated by breakAfter calls.
  1021. ++i) {
  1022. walk(listItems[i]);
  1023. }
  1024. // Make sure numeric indices show correctly.
  1025. if (opt_startLineNum === (opt_startLineNum|0)) {
  1026. listItems[0].setAttribute('value', opt_startLineNum);
  1027. }
  1028. var ol = document.createElement(opt_numberLines ? 'ol' : 'ul');
  1029. const classNames = [];
  1030. if (opt_numberLines) {
  1031. classNames.push('linenums');
  1032. }
  1033. if (opt_calloutModifiedLines) {
  1034. classNames.push('modifiedlines');
  1035. }
  1036. ol.className = classNames.join(" ");
  1037. var offset = Math.max(0, (opt_startLineNum - 1 /* zero index */) | 0) || 0;
  1038. for (var i = 0, n = listItems.length; i < n; ++i) {
  1039. li = listItems[i];
  1040. // Stick a class on the LIs so that stylesheets can
  1041. // color odd/even rows, or any other row pattern that
  1042. // is co-prime with 10.
  1043. const classNames = [];
  1044. if (opt_numberLines) {
  1045. classNames.push('L' + ((i + offset) % 10));
  1046. }
  1047. if (!li.firstChild) {
  1048. li.appendChild(document.createTextNode('\xA0'));
  1049. }
  1050. if (opt_calloutModifiedLines) {
  1051. // returns undefined to continue, true of found, false if not found
  1052. function findTextWithPrefix(node) {
  1053. if (!node) {
  1054. return;
  1055. }
  1056. var type = node.nodeType;
  1057. if (type == 1) { // Element
  1058. for (var child = node.firstChild; child; child = child.nextSibling) {
  1059. var result = findTextWithPrefix(child);
  1060. if (result !== undefined) {
  1061. return result;
  1062. }
  1063. }
  1064. } else if (type == 3 || type == 4) {
  1065. var text = node.nodeValue;
  1066. if (text.length > 0) {
  1067. for (var pp = 0; pp < prefixes.length; ++pp) {
  1068. var prefixInfo = prefixes[pp];
  1069. if (startsWith(text, prefixInfo.prefix)) {
  1070. node.nodeValue = text.substring(prefixInfo.prefix.length) || ' ';
  1071. return prefixInfo.className;
  1072. }
  1073. }
  1074. return false;
  1075. }
  1076. }
  1077. }
  1078. var foundPrefix = findTextWithPrefix(li);
  1079. if (foundPrefix) {
  1080. classNames.push(foundPrefix);
  1081. }
  1082. }
  1083. li.className = classNames.join(" ");
  1084. ol.appendChild(li);
  1085. }
  1086. node.appendChild(ol);
  1087. }
  1088. /**
  1089. * Breaks {@code job.sourceCode} around style boundaries in
  1090. * {@code job.decorations} and modifies {@code job.sourceNode} in place.
  1091. * @param {Object} job like <pre>{
  1092. * sourceCode: {string} source as plain text,
  1093. * sourceNode: {HTMLElement} the element containing the source,
  1094. * spans: {Array.<number|Node>} alternating span start indices into source
  1095. * and the text node or element (e.g. {@code <BR>}) corresponding to that
  1096. * span.
  1097. * decorations: {Array.<number|string} an array of style classes preceded
  1098. * by the position at which they start in job.sourceCode in order
  1099. * }</pre>
  1100. * @private
  1101. */
  1102. function recombineTagsAndDecorations(job) {
  1103. var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent);
  1104. isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8;
  1105. var newlineRe = /\n/g;
  1106. var source = job.sourceCode;
  1107. var sourceLength = source.length;
  1108. // Index into source after the last code-unit recombined.
  1109. var sourceIndex = 0;
  1110. var spans = job.spans;
  1111. var nSpans = spans.length;
  1112. // Index into spans after the last span which ends at or before sourceIndex.
  1113. var spanIndex = 0;
  1114. var decorations = job.decorations;
  1115. var nDecorations = decorations.length;
  1116. // Index into decorations after the last decoration which ends at or before
  1117. // sourceIndex.
  1118. var decorationIndex = 0;
  1119. // Remove all zero-length decorations.
  1120. decorations[nDecorations] = sourceLength;
  1121. var decPos, i;
  1122. for (i = decPos = 0; i < nDecorations;) {
  1123. if (decorations[i] !== decorations[i + 2]) {
  1124. decorations[decPos++] = decorations[i++];
  1125. decorations[decPos++] = decorations[i++];
  1126. } else {
  1127. i += 2;
  1128. }
  1129. }
  1130. nDecorations = decPos;
  1131. // Simplify decorations.
  1132. for (i = decPos = 0; i < nDecorations;) {
  1133. var startPos = decorations[i];
  1134. // Conflate all adjacent decorations that use the same style.
  1135. var startDec = decorations[i + 1];
  1136. let end = i + 2;
  1137. while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
  1138. end += 2;
  1139. }
  1140. decorations[decPos++] = startPos;
  1141. decorations[decPos++] = startDec;
  1142. i = end;
  1143. }
  1144. decorations.length = decPos;
  1145. var sourceNode = job.sourceNode;
  1146. var oldDisplay;
  1147. if (sourceNode) {
  1148. oldDisplay = sourceNode.style.display;
  1149. sourceNode.style.display = 'none';
  1150. }
  1151. try {
  1152. decoration = null;
  1153. while (spanIndex < nSpans) {
  1154. var spanEnd = spans[spanIndex + 2] || sourceLength;
  1155. var decEnd = decorations[decorationIndex + 2] || sourceLength;
  1156. const end = Math.min(spanEnd, decEnd);
  1157. var textNode = spans[spanIndex + 1];
  1158. var styledText;
  1159. if (textNode.nodeType !== 1 // Don't muck with <BR>s or <LI>s
  1160. // Don't introduce spans around empty text nodes.
  1161. && (styledText = source.substring(sourceIndex, end))) {
  1162. // This may seem bizarre, and it is. Emitting LF on IE causes the
  1163. // code to display with spaces instead of line breaks.
  1164. // Emitting Windows standard issue linebreaks (CRLF) causes a blank
  1165. // space to appear at the beginning of every line but the first.
  1166. // Emitting an old Mac OS 9 line separator makes everything spiffy.
  1167. if (isIE8OrEarlier) {
  1168. styledText = styledText.replace(newlineRe, '\r');
  1169. }
  1170. textNode.nodeValue = styledText;
  1171. var document = textNode.ownerDocument;
  1172. var span = document.createElement('span');
  1173. span.className = decorations[decorationIndex + 1];
  1174. var parentNode = textNode.parentNode;
  1175. parentNode.replaceChild(span, textNode);
  1176. span.appendChild(textNode);
  1177. if (sourceIndex < spanEnd) { // Split off a text node.
  1178. spans[spanIndex + 1] = textNode
  1179. // TODO: Possibly optimize by using '' if there's no flicker.
  1180. = document.createTextNode(source.substring(end, spanEnd));
  1181. parentNode.insertBefore(textNode, span.nextSibling);
  1182. }
  1183. }
  1184. sourceIndex = end;
  1185. if (sourceIndex >= spanEnd) {
  1186. spanIndex += 2;
  1187. }
  1188. if (sourceIndex >= decEnd) {
  1189. decorationIndex += 2;
  1190. }
  1191. }
  1192. } finally {
  1193. if (sourceNode) {
  1194. sourceNode.style.display = oldDisplay;
  1195. }
  1196. }
  1197. }
  1198. /** Maps language-specific file extensions to handlers. */
  1199. var langHandlerRegistry = {};
  1200. /** Register a language handler for the given file extensions.
  1201. * @param {function (Object)} handler a function from source code to a list
  1202. * of decorations. Takes a single argument job which describes the
  1203. * state of the computation. The single parameter has the form
  1204. * {@code {
  1205. * sourceCode: {string} as plain text.
  1206. * decorations: {Array.<number|string>} an array of style classes
  1207. * preceded by the position at which they start in
  1208. * job.sourceCode in order.
  1209. * The language handler should assigned this field.
  1210. * basePos: {int} the position of source in the larger source chunk.
  1211. * All positions in the output decorations array are relative
  1212. * to the larger source chunk.
  1213. * } }
  1214. * @param {Array.<string>} fileExtensions
  1215. */
  1216. function registerLangHandler(handler, fileExtensions) {
  1217. for (var i = fileExtensions.length; --i >= 0;) {
  1218. var ext = fileExtensions[i];
  1219. if (!langHandlerRegistry.hasOwnProperty(ext)) {
  1220. langHandlerRegistry[ext] = handler;
  1221. } else if (win['console']) {
  1222. console['warn']('cannot override language handler %s', ext);
  1223. }
  1224. }
  1225. }
  1226. function langHandlerForExtension(extension, source) {
  1227. if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
  1228. // Treat it as markup if the first non whitespace character is a < and
  1229. // the last non-whitespace character is a >.
  1230. extension = /^\s*</.test(source)
  1231. ? 'default-markup'
  1232. : 'default-code';
  1233. }
  1234. return langHandlerRegistry[extension];
  1235. }
  1236. registerLangHandler(decorateSource, ['default-code']);
  1237. registerLangHandler(
  1238. createSimpleLexer(
  1239. [],
  1240. [
  1241. [PR_PLAIN, /^[^<?]+/],
  1242. [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
  1243. [PR_COMMENT, /^<\!--[\s\S]*?(?:-\->|$)/],
  1244. // Unescaped content in an unknown language
  1245. ['lang-', /^<\?([\s\S]+?)(?:\?>|$)/],
  1246. ['lang-', /^<%([\s\S]+?)(?:%>|$)/],
  1247. [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
  1248. ['lang-', /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
  1249. // Unescaped content in javascript. (Or possibly vbscript).
  1250. ['lang-js', /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
  1251. // Contains unescaped stylesheet content
  1252. ['lang-css', /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
  1253. ['lang-in.tag', /^(<\/?[a-z][^<>]*>)/i]
  1254. ]),
  1255. ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
  1256. registerLangHandler(
  1257. createSimpleLexer(
  1258. [
  1259. [PR_PLAIN, /^[\s]+/, null, ' \t\r\n'],
  1260. [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
  1261. ],
  1262. [
  1263. [PR_TAG, /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
  1264. [PR_ATTRIB_NAME, /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
  1265. ['lang-uq.val', /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
  1266. [PR_PUNCTUATION, /^[=<>\/]+/],
  1267. ['lang-js', /^on\w+\s*=\s*\"([^\"]+)\"/i],
  1268. ['lang-js', /^on\w+\s*=\s*\'([^\']+)\'/i],
  1269. ['lang-js', /^on\w+\s*=\s*([^\"\'>\s]+)/i],
  1270. ['lang-css', /^style\s*=\s*\"([^\"]+)\"/i],
  1271. ['lang-css', /^style\s*=\s*\'([^\']+)\'/i],
  1272. ['lang-css', /^style\s*=\s*([^\"\'>\s]+)/i]
  1273. ]),
  1274. ['in.tag']);
  1275. registerLangHandler(
  1276. createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
  1277. registerLangHandler(sourceDecorator({
  1278. 'keywords': CPP_KEYWORDS,
  1279. 'hashComments': true,
  1280. 'cStyleComments': true,
  1281. 'types': C_TYPES
  1282. }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
  1283. registerLangHandler(sourceDecorator({
  1284. 'keywords': 'null,true,false'
  1285. }), ['json']);
  1286. registerLangHandler(sourceDecorator({
  1287. 'keywords': CSHARP_KEYWORDS,
  1288. 'hashComments': true,
  1289. 'cStyleComments': true,
  1290. 'verbatimStrings': true,
  1291. 'types': C_TYPES
  1292. }), ['cs']);
  1293. registerLangHandler(sourceDecorator({
  1294. 'keywords': JAVA_KEYWORDS,
  1295. 'cStyleComments': true
  1296. }), ['java']);
  1297. registerLangHandler(sourceDecorator({
  1298. 'keywords': SH_KEYWORDS,
  1299. 'hashComments': true,
  1300. 'multiLineStrings': true
  1301. }), ['bash', 'bsh', 'csh', 'sh']);
  1302. registerLangHandler(sourceDecorator({
  1303. 'keywords': PYTHON_KEYWORDS,
  1304. 'hashComments': true,
  1305. 'multiLineStrings': true,
  1306. 'tripleQuotedStrings': true
  1307. }), ['cv', 'py', 'python']);
  1308. registerLangHandler(sourceDecorator({
  1309. 'keywords': PERL_KEYWORDS,
  1310. 'hashComments': true,
  1311. 'multiLineStrings': true,
  1312. 'regexLiterals': 2 // multiline regex literals
  1313. }), ['perl', 'pl', 'pm']);
  1314. registerLangHandler(sourceDecorator({
  1315. 'keywords': RUBY_KEYWORDS,
  1316. 'hashComments': true,
  1317. 'multiLineStrings': true,
  1318. 'regexLiterals': true
  1319. }), ['rb', 'ruby']);
  1320. registerLangHandler(sourceDecorator({
  1321. 'keywords': JSCRIPT_KEYWORDS,
  1322. 'cStyleComments': true,
  1323. 'regexLiterals': true
  1324. }), ['javascript', 'js']);
  1325. registerLangHandler(sourceDecorator({
  1326. 'keywords': COFFEE_KEYWORDS,
  1327. 'hashComments': 3, // ### style block comments
  1328. 'cStyleComments': true,
  1329. 'multilineStrings': true,
  1330. 'tripleQuotedStrings': true,
  1331. 'regexLiterals': true
  1332. }), ['coffee']);
  1333. registerLangHandler(sourceDecorator({
  1334. 'keywords': RUST_KEYWORDS,
  1335. 'cStyleComments': true,
  1336. 'multilineStrings': true
  1337. }), ['rc', 'rs', 'rust']);
  1338. registerLangHandler(
  1339. createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
  1340. function applyDecorator(job) {
  1341. var opt_langExtension = job.langExtension;
  1342. try {
  1343. // Extract tags, and convert the source code to plain text.
  1344. var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
  1345. /** Plain text. @type {string} */
  1346. var source = sourceAndSpans.sourceCode;
  1347. job.sourceCode = source;
  1348. job.spans = sourceAndSpans.spans;
  1349. job.basePos = 0;
  1350. // Apply the appropriate language handler
  1351. langHandlerForExtension(opt_langExtension, source)(job);
  1352. // Integrate the decorations and tags back into the source code,
  1353. // modifying the sourceNode in place.
  1354. recombineTagsAndDecorations(job);
  1355. } catch (e) {
  1356. if (win['console']) {
  1357. console['log'](e && e['stack'] || e);
  1358. }
  1359. }
  1360. }
  1361. /**
  1362. * Pretty print a chunk of code.
  1363. * @param sourceCodeHtml {string} The HTML to pretty print.
  1364. * @param opt_langExtension {string} The language name to use.
  1365. * Typically, a filename extension like 'cpp' or 'java'.
  1366. * @param opt_numberLines {number|boolean} True to number lines,
  1367. * or the 1-indexed number of the first line in sourceCodeHtml.
  1368. */
  1369. function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
  1370. var container = document.createElement('div');
  1371. // This could cause images to load and onload listeners to fire.
  1372. // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
  1373. // We assume that the inner HTML is from a trusted source.
  1374. // The pre-tag is required for IE8 which strips newlines from innerHTML
  1375. // when it is injected into a <pre> tag.
  1376. // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
  1377. // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
  1378. container.innerHTML = '<pre>' + sourceCodeHtml + '</pre>';
  1379. container = container.firstChild;
  1380. if (opt_numberLines) {
  1381. numberLines(container, opt_numberLines, true);
  1382. }
  1383. var job = {
  1384. langExtension: opt_langExtension,
  1385. numberLines: opt_numberLines,
  1386. sourceNode: container,
  1387. pre: 1
  1388. };
  1389. applyDecorator(job);
  1390. return container.innerHTML;
  1391. }
  1392. /**
  1393. * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
  1394. * {@code class=prettyprint} and prettify them.
  1395. *
  1396. * @param {Function} opt_whenDone called when prettifying is done.
  1397. * @param {HTMLElement|HTMLDocument} opt_root an element or document
  1398. * containing all the elements to pretty print.
  1399. * Defaults to {@code document.body}.
  1400. */
  1401. function $prettyPrint(opt_whenDone, opt_root) {
  1402. var root = opt_root || document.body;
  1403. var doc = root.ownerDocument || document;
  1404. function byTagName(tn) { return root.getElementsByTagName(tn); }
  1405. // fetch a list of nodes to rewrite
  1406. var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
  1407. var elements = [];
  1408. for (var i = 0; i < codeSegments.length; ++i) {
  1409. for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
  1410. elements.push(codeSegments[i][j]);
  1411. }
  1412. }
  1413. codeSegments = null;
  1414. var clock = Date;
  1415. if (!clock['now']) {
  1416. clock = { 'now': function () { return +(new Date); } };
  1417. }
  1418. // The loop is broken into a series of continuations to make sure that we
  1419. // don't make the browser unresponsive when rewriting a large page.
  1420. var k = 0;
  1421. var prettyPrintingJob;
  1422. var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
  1423. var prettyPrintRe = /\bprettyprint\b/;
  1424. var prettyPrintedRe = /\bprettyprinted\b/;
  1425. var preformattedTagNameRe = /pre|xmp/i;
  1426. var codeRe = /^code$/i;
  1427. var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
  1428. var EMPTY = {};
  1429. function doWork() {
  1430. var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
  1431. clock['now']() + 250 /* ms */ :
  1432. Infinity);
  1433. for (; k < elements.length && clock['now']() < endTime; k++) {
  1434. var cs = elements[k];
  1435. // Look for a preceding comment like
  1436. // <?prettify lang="..." linenums="..."?>
  1437. var attrs = EMPTY;
  1438. {
  1439. for (var preceder = cs; (preceder = preceder.previousSibling);) {
  1440. var nt = preceder.nodeType;
  1441. // <?foo?> is parsed by HTML 5 to a comment node (8)
  1442. // like <!--?foo?-->, but in XML is a processing instruction
  1443. var value = (nt === 7 || nt === 8) && preceder.nodeValue;
  1444. if (value
  1445. ? !/^\??prettify\b/.test(value)
  1446. : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
  1447. // Skip over white-space text nodes but not others.
  1448. break;
  1449. }
  1450. if (value) {
  1451. attrs = {};
  1452. value.replace(
  1453. /\b(\w+)=([\w:.%+-]+)/g,
  1454. function (_, name, value) { attrs[name] = value; });
  1455. break;
  1456. }
  1457. }
  1458. }
  1459. var className = cs.className;
  1460. if ((attrs !== EMPTY || prettyPrintRe.test(className))
  1461. // Don't redo this if we've already done it.
  1462. // This allows recalling pretty print to just prettyprint elements
  1463. // that have been added to the page since last call.
  1464. && !prettyPrintedRe.test(className)) {
  1465. // make sure this is not nested in an already prettified element
  1466. var nested = false;
  1467. for (var p = cs.parentNode; p; p = p.parentNode) {
  1468. var tn = p.tagName;
  1469. if (preCodeXmpRe.test(tn)
  1470. && p.className && prettyPrintRe.test(p.className)) {
  1471. nested = true;
  1472. break;
  1473. }
  1474. }
  1475. if (!nested) {
  1476. // Mark done. If we fail to prettyprint for whatever reason,
  1477. // we shouldn't try again.
  1478. cs.className += ' prettyprinted';
  1479. // If the classes includes a language extensions, use it.
  1480. // Language extensions can be specified like
  1481. // <pre class="prettyprint lang-cpp">
  1482. // the language extension "cpp" is used to find a language handler
  1483. // as passed to PR.registerLangHandler.
  1484. // HTML5 recommends that a language be specified using "language-"
  1485. // as the prefix instead. Google Code Prettify supports both.
  1486. // http://dev.w3.org/html5/spec-author-view/the-code-element.html
  1487. var langExtension = attrs['lang'];
  1488. if (!langExtension) {
  1489. langExtension = className.match(langExtensionRe);
  1490. // Support <pre class="prettyprint"><code class="language-c">
  1491. var wrapper;
  1492. if (!langExtension && (wrapper = childContentWrapper(cs))
  1493. && codeRe.test(wrapper.tagName)) {
  1494. langExtension = wrapper.className.match(langExtensionRe);
  1495. }
  1496. if (langExtension) { langExtension = langExtension[1]; }
  1497. }
  1498. var preformatted;
  1499. if (preformattedTagNameRe.test(cs.tagName)) {
  1500. preformatted = 1;
  1501. } else {
  1502. var currentStyle = cs['currentStyle'];
  1503. var defaultView = doc.defaultView;
  1504. var whitespace = (
  1505. currentStyle
  1506. ? currentStyle['whiteSpace']
  1507. : (defaultView
  1508. && defaultView.getComputedStyle)
  1509. ? defaultView.getComputedStyle(cs, null)
  1510. .getPropertyValue('white-space')
  1511. : 0);
  1512. preformatted = whitespace
  1513. && 'pre' === whitespace.substring(0, 3);
  1514. }
  1515. // Look for a class like linenums or linenums:<n> where <n> is the
  1516. // 1-indexed number of the first line.
  1517. var lineNums = attrs['linenums'];
  1518. if (!(lineNums = lineNums === 'true' || +lineNums)) {
  1519. lineNums = className.match(/\blinenums\b(?::(\d+))?/);
  1520. lineNums =
  1521. lineNums
  1522. ? lineNums[1] && lineNums[1].length
  1523. ? +lineNums[1] : true
  1524. : false;
  1525. }
  1526. var showLineMods = attrs['showlinemods'];
  1527. if (showLineMods === undefined) {
  1528. showLineMods = className.match(/\bshowlinemods\b/);
  1529. }
  1530. if (lineNums || showLineMods) { numberLines(cs, lineNums, preformatted, lineNums, showLineMods); }
  1531. // do the pretty printing
  1532. prettyPrintingJob = {
  1533. langExtension: langExtension,
  1534. sourceNode: cs,
  1535. numberLines: lineNums,
  1536. pre: preformatted
  1537. };
  1538. applyDecorator(prettyPrintingJob);
  1539. }
  1540. }
  1541. }
  1542. if (k < elements.length) {
  1543. // finish up in a continuation
  1544. setTimeout(doWork, 250);
  1545. } else if ('function' === typeof opt_whenDone) {
  1546. opt_whenDone();
  1547. }
  1548. }
  1549. doWork();
  1550. }
  1551. /**
  1552. * Contains functions for creating and registering new language handlers.
  1553. * @type {Object}
  1554. */
  1555. var PR = win['PR'] = {
  1556. 'createSimpleLexer': createSimpleLexer,
  1557. 'registerLangHandler': registerLangHandler,
  1558. 'sourceDecorator': sourceDecorator,
  1559. 'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
  1560. 'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
  1561. 'PR_COMMENT': PR_COMMENT,
  1562. 'PR_DECLARATION': PR_DECLARATION,
  1563. 'PR_KEYWORD': PR_KEYWORD,
  1564. 'PR_LITERAL': PR_LITERAL,
  1565. 'PR_NOCODE': PR_NOCODE,
  1566. 'PR_PLAIN': PR_PLAIN,
  1567. 'PR_PUNCTUATION': PR_PUNCTUATION,
  1568. 'PR_SOURCE': PR_SOURCE,
  1569. 'PR_STRING': PR_STRING,
  1570. 'PR_TAG': PR_TAG,
  1571. 'PR_TYPE': PR_TYPE,
  1572. 'prettyPrintOne':
  1573. IN_GLOBAL_SCOPE
  1574. ? (win['prettyPrintOne'] = $prettyPrintOne)
  1575. : (prettyPrintOne = $prettyPrintOne),
  1576. 'prettyPrint':
  1577. IN_GLOBAL_SCOPE
  1578. ? (prettyPrint = win['prettyPrint'] = $prettyPrint)
  1579. : (prettyPrint = $prettyPrint)
  1580. };
  1581. // Make PR available via the Asynchronous Module Definition (AMD) API.
  1582. // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
  1583. // The Asynchronous Module Definition (AMD) API specifies a
  1584. // mechanism for defining modules such that the module and its
  1585. // dependencies can be asynchronously loaded.
  1586. // ...
  1587. // To allow a clear indicator that a global define function (as
  1588. // needed for script src browser loading) conforms to the AMD API,
  1589. // any global define function SHOULD have a property called "amd"
  1590. // whose value is an object. This helps avoid conflict with any
  1591. // other existing JavaScript code that could have defined a define()
  1592. // function that does not conform to the AMD API.
  1593. if (typeof define === "function" && define['amd']) {
  1594. define("google-code-prettify", [], function () {
  1595. return PR;
  1596. });
  1597. }
  1598. })();