2 * 2 way binding code mirror for AngularJS based on google-prettify
3 * @version v0.0.3 - 2014-09-21 * @link https://github.com/a8m/angular-code-mirror
4 * @author Ariel Mashraki <ariel@mashraki.co.il>
5 * @license MIT License, http://www.opensource.org/licenses/MIT
7 (function ( window, angular, undefined ) {
8 /*jshint globalstrict:true*/
11 var isDefined = angular.isDefined,
12 isUndefined = angular.isUndefined,
13 isFunction = angular.isFunction,
14 isString = angular.isString,
15 isNumber = angular.isNumber,
16 isObject = angular.isObject,
17 isArray = angular.isArray,
18 forEach = angular.forEach,
19 extend = angular.extend,
21 equals = angular.equals,
22 lowercase = angular.lowercase,
23 uppercase = angular.uppercase;
28 * @name ng-code-mirror.directive
31 * angular code mirror directive
33 angular.module('ng-code-mirror.directive', ['ng-code-mirror.prettify'])
34 .directive('codeMirror', ['prettify', codeMirrorDirective]);
41 * <code-mirror land="java" model="scope.model"></code-mirror>
43 function codeMirrorDirective(prettify) {
46 compile: function(tElm, tAttr, transcluse) {
48 //create <pre> root element and bind it prettify class
49 var preElm = angular.element('<pre></pre>')
50 .addClass('prettyprint');
52 //create <code> element and bind it appropriate class
53 var codeElm = angular.element('<code></code>')
54 .addClass('language-' + lowercase(tAttr.lang));
56 preElm.append(codeElm);
57 //replace tElm with new preElm
58 tElm.replaceWith(preElm[0]);
66 * Directive link function
71 function linkFn(scope, elm, attr) {
74 var codeElm = elm.find('code');
75 //determine if to add line numbers or not
76 var lineNumbers = scope.$eval(attr.lineNumbers) || false;
78 scope.$watch(attr.model, function(nVal) {
81 //replace all tag chars with their entity
82 codeElm.html(prettify.one(
83 nVal.replace(/</g, '<').replace(/>/g,'>'),
96 * @name ng-code-mirror
99 * angular code mirror module
101 angular.module('ng-code-mirror', [
102 'ng-code-mirror.directive',
103 'ng-code-mirror.prettify'
109 * @name ng-code-mirror.service
114 angular.module('ng-code-mirror.prettify', [])
115 .provider('prettify', prettifyProvider);
120 * wrap global google prettify api
122 function prettifyProvider() {
124 this.$get = ['$window', function($window) {
127 global: $window.prettyPrint,
128 one: $window.prettyPrintOne
134 // Copyright (C) 2006 Google Inc.
136 // Licensed under the Apache License, Version 2.0 (the "License");
137 // you may not use this file except in compliance with the License.
138 // You may obtain a copy of the License at
140 // http://www.apache.org/licenses/LICENSE-2.0
142 // Unless required by applicable law or agreed to in writing, software
143 // distributed under the License is distributed on an "AS IS" BASIS,
144 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
145 // See the License for the specific language governing permissions and
146 // limitations under the License.
151 * some functions for browser-side pretty printing of code contained in html.
154 * For a fairly comprehensive set of languages see the
155 * <a href="http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs">README</a>
156 * file that came with this source. At a minimum, the lexer should work on a
157 * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
158 * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk
159 * and a subset of Perl, but, because of commenting conventions, doesn't work on
160 * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
163 * <li> include this source file in an html page via
164 * {@code <script type="text/javascript" src="/path/to/prettify.js"></script>}
165 * <li> define style rules. See the example page for examples.
166 * <li> mark the {@code <pre>} and {@code <code>} tags in your source with
167 * {@code class=prettyprint.}
168 * You can also use the (html deprecated) {@code <xmp>} tag, but the pretty
169 * printer needs to do more substantial DOM manipulations to support that, so
170 * some css styles may not be preserved.
172 * That's it. I wanted to keep the API as simple as possible, so there's no
173 * need to specify which language the code is in, but if you wish, you can add
174 * another class to the {@code <pre>} or {@code <code>} element to specify the
175 * language, as in {@code <pre class="prettyprint lang-java">}. Any class that
176 * starts with "lang-" followed by a file extension, specifies the file type.
177 * See the "lang-*.js" files in this directory for code that implements
178 * per-language file handlers.
183 * Java annotations (start with "@") are now captured as literals ("lit")
188 // JSLint declarations
189 /*global console, document, navigator, setTimeout, window, define */
191 /** @define {boolean} */
192 var IN_GLOBAL_SCOPE = true;
195 * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
197 * If set to {@code false}, {@code prettyPrint()} is synchronous.
199 window['PR_SHOULD_USE_CONTINUATION'] = true;
202 * Pretty print a chunk of code.
203 * @param {string} sourceCodeHtml The HTML to pretty print.
204 * @param {string} opt_langExtension The language name to use.
205 * Typically, a filename extension like 'cpp' or 'java'.
206 * @param {number|boolean} opt_numberLines True to number lines,
207 * or the 1-indexed number of the first line in sourceCodeHtml.
208 * @return {string} code as html, but prettier
212 * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
213 * {@code class=prettyprint} and prettify them.
215 * @param {Function} opt_whenDone called when prettifying is done.
216 * @param {HTMLElement|HTMLDocument} opt_root an element or document
217 * containing all the elements to pretty print.
218 * Defaults to {@code document.body}.
225 // Keyword lists for various languages.
226 // We use things that coerce to strings to make them compact when minified
227 // and to defeat aggressive optimizers that fold large string constants.
228 var FLOW_CONTROL_KEYWORDS = ["break,continue,do,else,for,if,return,while"];
229 var C_KEYWORDS = [FLOW_CONTROL_KEYWORDS,"auto,case,char,const,default," +
230 "double,enum,extern,float,goto,inline,int,long,register,short,signed," +
231 "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];
232 var COMMON_KEYWORDS = [C_KEYWORDS,"catch,class,delete,false,import," +
233 "new,operator,private,protected,public,this,throw,true,try,typeof"];
234 var CPP_KEYWORDS = [COMMON_KEYWORDS,"alignof,align_union,asm,axiom,bool," +
235 "concept,concept_map,const_cast,constexpr,decltype,delegate," +
236 "dynamic_cast,explicit,export,friend,generic,late_check," +
237 "mutable,namespace,nullptr,property,reinterpret_cast,static_assert," +
238 "static_cast,template,typeid,typename,using,virtual,where"];
239 var JAVA_KEYWORDS = [COMMON_KEYWORDS,
240 "abstract,assert,boolean,byte,extends,final,finally,implements,import," +
241 "instanceof,interface,null,native,package,strictfp,super,synchronized," +
243 var CSHARP_KEYWORDS = [JAVA_KEYWORDS,
244 "as,base,by,checked,decimal,delegate,descending,dynamic,event," +
245 "fixed,foreach,from,group,implicit,in,internal,into,is,let," +
246 "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte," +
247 "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort," +
248 "var,virtual,where"];
249 var COFFEE_KEYWORDS = "all,and,by,catch,class,else,extends,false,finally," +
250 "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then," +
251 "throw,true,try,unless,until,when,while,yes";
252 var JSCRIPT_KEYWORDS = [COMMON_KEYWORDS,
253 "debugger,eval,export,function,get,null,set,undefined,var,with," +
255 var PERL_KEYWORDS = "caller,delete,die,do,dump,elsif,eval,exit,foreach,for," +
256 "goto,if,import,last,local,my,next,no,our,print,package,redo,require," +
257 "sub,undef,unless,until,use,wantarray,while,BEGIN,END";
258 var PYTHON_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "and,as,assert,class,def,del," +
259 "elif,except,exec,finally,from,global,import,in,is,lambda," +
260 "nonlocal,not,or,pass,print,raise,try,with,yield," +
262 var RUBY_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "alias,and,begin,case,class," +
263 "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo," +
264 "rescue,retry,self,super,then,true,undef,unless,until,when,yield," +
266 var RUST_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "as,assert,const,copy,drop," +
267 "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv," +
268 "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use"];
269 var SH_KEYWORDS = [FLOW_CONTROL_KEYWORDS, "case,done,elif,esac,eval,fi," +
270 "function,in,local,set,then,until"];
272 CPP_KEYWORDS, CSHARP_KEYWORDS, JSCRIPT_KEYWORDS, PERL_KEYWORDS,
273 PYTHON_KEYWORDS, RUBY_KEYWORDS, SH_KEYWORDS];
274 var C_TYPES = /^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)\b/;
276 // token style names. correspond to css classes
278 * token style for a string literal
281 var PR_STRING = 'str';
283 * token style for a keyword
286 var PR_KEYWORD = 'kwd';
288 * token style for a comment
291 var PR_COMMENT = 'com';
293 * token style for a type
298 * token style for a literal value. e.g. 1, null, true.
301 var PR_LITERAL = 'lit';
303 * token style for a punctuation string.
306 var PR_PUNCTUATION = 'pun';
308 * token style for plain text.
311 var PR_PLAIN = 'pln';
314 * token style for an sgml tag.
319 * token style for a markup declaration such as a DOCTYPE.
322 var PR_DECLARATION = 'dec';
324 * token style for embedded source.
327 var PR_SOURCE = 'src';
329 * token style for an sgml attribute name.
332 var PR_ATTRIB_NAME = 'atn';
334 * token style for an sgml attribute value.
337 var PR_ATTRIB_VALUE = 'atv';
340 * A class that indicates a section of markup that is not code, e.g. to allow
341 * embedding of line numbers within code listings.
344 var PR_NOCODE = 'nocode';
349 * A set of tokens that can precede a regular expression literal in
351 * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
352 * has the full list, but I've removed ones that might be problematic when
353 * seen in languages that don't support regular expression literals.
355 * <p>Specifically, I've removed any keywords that can't precede a regexp
356 * literal in a syntactically legal javascript program, and I've removed the
357 * "in" keyword since it's not a keyword in many languages, and might be used
358 * as a count of inches.
360 * <p>The link above does not accurately describe EcmaScript rules since
361 * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
362 * very well in practice.
367 var REGEXP_PRECEDER_PATTERN = '(?:^^\\.?|[+-]|[!=]=?=?|\\#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|\\/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*';
369 // CAVEAT: this does not properly handle the case where a regular
370 // expression immediately follows another since a regular expression may
371 // have flags for case-sensitivity and the like. Having regexp tokens
372 // adjacent is not valid in any language I'm aware of, so I'm punting.
373 // TODO: maybe style special characters inside a regexp as punctuation.
376 * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
377 * matches the union of the sets of strings matched by the input RegExp.
378 * Since it matches globally, if the input strings have a start-of-input
379 * anchor (/^.../), it is ignored for the purposes of unioning.
380 * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
381 * @return {RegExp} a global regex.
383 function combinePrefixPatterns(regexs) {
384 var capturedGroupIndex = 0;
386 var needToFoldCase = false;
387 var ignoreCase = false;
388 for (var i = 0, n = regexs.length; i < n; ++i) {
389 var regex = regexs[i];
390 if (regex.ignoreCase) {
392 } else if (/[a-z]/i.test(regex.source.replace(
393 /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
394 needToFoldCase = true;
400 var escapeCharToCodeUnit = {
409 function decodeEscape(charsetPart) {
410 var cc0 = charsetPart.charCodeAt(0);
411 if (cc0 !== 92 /* \\ */) {
414 var c1 = charsetPart.charAt(1);
415 cc0 = escapeCharToCodeUnit[c1];
418 } else if ('0' <= c1 && c1 <= '7') {
419 return parseInt(charsetPart.substring(1), 8);
420 } else if (c1 === 'u' || c1 === 'x') {
421 return parseInt(charsetPart.substring(2), 16);
423 return charsetPart.charCodeAt(1);
427 function encodeEscape(charCode) {
428 if (charCode < 0x20) {
429 return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
431 var ch = String.fromCharCode(charCode);
432 return (ch === '\\' || ch === '-' || ch === ']' || ch === '^')
436 function caseFoldCharset(charSet) {
437 var charsetParts = charSet.substring(1, charSet.length - 1).match(
439 '\\\\u[0-9A-Fa-f]{4}'
440 + '|\\\\x[0-9A-Fa-f]{2}'
441 + '|\\\\[0-3][0-7]{0,2}'
448 var inverse = charsetParts[0] === '^';
451 if (inverse) { out.push('^'); }
453 for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
454 var p = charsetParts[i];
455 if (/\\[bdsw]/i.test(p)) { // Don't muck with named groups.
458 var start = decodeEscape(p);
460 if (i + 2 < n && '-' === charsetParts[i + 1]) {
461 end = decodeEscape(charsetParts[i + 2]);
466 ranges.push([start, end]);
467 // If the range might intersect letters, then expand it.
468 // This case handling is too simplistic.
469 // It does not deal with non-latin case folding.
470 // It works for latin source code identifiers though.
471 if (!(end < 65 || start > 122)) {
472 if (!(end < 65 || start > 90)) {
473 ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
475 if (!(end < 97 || start > 122)) {
476 ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
482 // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
483 // -> [[1, 12], [14, 14], [16, 17]]
484 ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1] - a[1]); });
485 var consolidatedRanges = [];
487 for (var i = 0; i < ranges.length; ++i) {
488 var range = ranges[i];
489 if (range[0] <= lastRange[1] + 1) {
490 lastRange[1] = Math.max(lastRange[1], range[1]);
492 consolidatedRanges.push(lastRange = range);
496 for (var i = 0; i < consolidatedRanges.length; ++i) {
497 var range = consolidatedRanges[i];
498 out.push(encodeEscape(range[0]));
499 if (range[1] > range[0]) {
500 if (range[1] + 1 > range[0]) { out.push('-'); }
501 out.push(encodeEscape(range[1]));
508 function allowAnywhereFoldCaseAndRenumberGroups(regex) {
509 // Split into character sets, escape sequences, punctuation strings
510 // like ('(', '(?:', ')', '^'), and runs of characters that do not
511 // include any of the above.
512 var parts = regex.source.match(
515 + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]' // a character set
516 + '|\\\\u[A-Fa-f0-9]{4}' // a unicode escape
517 + '|\\\\x[A-Fa-f0-9]{2}' // a hex escape
518 + '|\\\\[0-9]+' // a back-reference or octal escape
519 + '|\\\\[^ux0-9]' // other escape sequence
520 + '|\\(\\?[:!=]' // start of a non-capturing group
521 + '|[\\(\\)\\^]' // start/end of a group, or line start
522 + '|[^\\x5B\\x5C\\(\\)\\^]+' // run of other characters
525 var n = parts.length;
527 // Maps captured group numbers to the number they will occupy in
528 // the output or to -1 if that has not been determined, or to
529 // undefined if they need not be capturing in the output.
530 var capturedGroups = [];
532 // Walk over and identify back references to build the capturedGroups
534 for (var i = 0, groupIndex = 0; i < n; ++i) {
537 // groups are 1-indexed, so max group index is count of '('
539 } else if ('\\' === p.charAt(0)) {
540 var decimalValue = +p.substring(1);
542 if (decimalValue <= groupIndex) {
543 capturedGroups[decimalValue] = -1;
545 // Replace with an unambiguous escape sequence so that
546 // an octal escape sequence does not turn into a backreference
547 // to a capturing group from an earlier regex.
548 parts[i] = encodeEscape(decimalValue);
554 // Renumber groups and reduce capturing groups to non-capturing groups
556 for (var i = 1; i < capturedGroups.length; ++i) {
557 if (-1 === capturedGroups[i]) {
558 capturedGroups[i] = ++capturedGroupIndex;
561 for (var i = 0, groupIndex = 0; i < n; ++i) {
565 if (!capturedGroups[groupIndex]) {
568 } else if ('\\' === p.charAt(0)) {
569 var decimalValue = +p.substring(1);
570 if (decimalValue && decimalValue <= groupIndex) {
571 parts[i] = '\\' + capturedGroups[decimalValue];
576 // Remove any prefix anchors so that the output will match anywhere.
577 // ^^ really does mean an anchored match though.
578 for (var i = 0; i < n; ++i) {
579 if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
582 // Expand letters to groups to handle mixing of case-sensitive and
583 // case-insensitive patterns if necessary.
584 if (regex.ignoreCase && needToFoldCase) {
585 for (var i = 0; i < n; ++i) {
587 var ch0 = p.charAt(0);
588 if (p.length >= 2 && ch0 === '[') {
589 parts[i] = caseFoldCharset(p);
590 } else if (ch0 !== '\\') {
591 // TODO: handle letters in numeric escapes.
592 parts[i] = p.replace(
595 var cc = ch.charCodeAt(0);
596 return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
602 return parts.join('');
606 for (var i = 0, n = regexs.length; i < n; ++i) {
607 var regex = regexs[i];
608 if (regex.global || regex.multiline) { throw new Error('' + regex); }
610 '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
613 return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
617 * Split markup into a string of source code and an array mapping ranges in
618 * that string to the text nodes in which they appear.
621 * The HTML DOM structure:</p>
625 * (Text "print ")) ; #1
626 * (Text "'Hello '") ; #2
627 * (Element "br") ; #3
628 * (Text " + 'World';")) ; #4
631 * corresponds to the HTML
632 * {@code <p><b>print </b>'Hello '<br> + 'World';</p>}.</p>
635 * It will produce the output:</p>
638 * sourceCode: "print 'Hello '\n + 'World';",
640 * // 012345678901234 5678901234567
641 * spans: [0, #1, 6, #2, 14, #3, 15, #4]
645 * where #1 is a reference to the {@code "print "} text node above, and so
646 * on for the other text nodes.
650 * The {@code} spans array is an array of pairs. Even elements are the start
651 * indices of substrings, and odd elements are the text nodes (or BR elements)
652 * that contain the text for those substrings.
653 * Substrings continue until the next index or the end of the source.
656 * @param {Node} node an HTML DOM subtree containing source-code.
657 * @param {boolean} isPreformatted true if white-space in text nodes should
658 * be considered significant.
659 * @return {Object} source code and the text nodes in which they occur.
661 function extractSourceSpans(node, isPreformatted) {
662 var nocode = /(?:^|\s)nocode(?:\s|$)/;
669 function walk(node) {
670 var type = node.nodeType;
671 if (type == 1) { // Element
672 if (nocode.test(node.className)) { return; }
673 for (var child = node.firstChild; child; child = child.nextSibling) {
676 var nodeName = node.nodeName.toLowerCase();
677 if ('br' === nodeName || 'li' === nodeName) {
679 spans[k << 1] = length++;
680 spans[(k++ << 1) | 1] = node;
682 } else if (type == 3 || type == 4) { // Text
683 var text = node.nodeValue;
685 if (!isPreformatted) {
686 text = text.replace(/[ \t\r\n]+/g, ' ');
688 text = text.replace(/\r\n?/g, '\n'); // Normalize newlines.
690 // TODO: handle tabs here?
692 spans[k << 1] = length;
693 length += text.length;
694 spans[(k++ << 1) | 1] = node;
702 sourceCode: chunks.join('').replace(/\n$/, ''),
708 * Apply the given language handler to sourceCode and add the resulting
709 * decorations to out.
710 * @param {number} basePos the index of sourceCode within the chunk of source
711 * whose decorations are already present on out.
713 function appendDecorations(basePos, sourceCode, langHandler, out) {
714 if (!sourceCode) { return; }
716 sourceCode: sourceCode,
720 out.push.apply(out, job.decorations);
726 * Given an element, if it contains only one child element and any text nodes
727 * it contains contain only space characters, return the sole child element.
728 * Otherwise returns undefined.
730 * This is meant to return the CODE element in {@code <pre><code ...>} when
731 * there is a single child element that contains all the non-space textual
732 * content, but not to return anything where there are multiple child elements
733 * as in {@code <pre><code>...</code><code>...</code></pre>} or when there
734 * is textual content.
736 function childContentWrapper(element) {
737 var wrapper = undefined;
738 for (var c = element.firstChild; c; c = c.nextSibling) {
739 var type = c.nodeType;
740 wrapper = (type === 1) // Element Node
741 ? (wrapper ? element : c)
742 : (type === 3) // Text Node
743 ? (notWs.test(c.nodeValue) ? element : wrapper)
746 return wrapper === element ? undefined : wrapper;
749 /** Given triples of [style, pattern, context] returns a lexing function,
750 * The lexing function interprets the patterns to find token boundaries and
751 * returns a decoration list of the form
752 * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
753 * where index_n is an index into the sourceCode, and style_n is a style
754 * constant like PR_PLAIN. index_n-1 <= index_n, and style_n-1 applies to
755 * all characters in sourceCode[index_n-1:index_n].
757 * The stylePatterns is a list whose elements have the form
758 * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
760 * Style is a style constant like PR_PLAIN, or can be a string of the
761 * form 'lang-FOO', where FOO is a language extension describing the
762 * language of the portion of the token in $1 after pattern executes.
763 * E.g., if style is 'lang-lisp', and group 1 contains the text
764 * '(hello (world))', then that portion of the token will be passed to the
765 * registered lisp handler for formatting.
766 * The text before and after group 1 will be restyled using this decorator
767 * so decorators should take care that this doesn't result in infinite
768 * recursion. For example, the HTML lexer rule for SCRIPT elements looks
769 * something like ['lang-js', /<[s]cript>(.+?)<\/script>/]. This may match
770 * '<script>foo()<\/script>', which would cause the current decorator to
771 * be called with '<script>' which would not match the same rule since
772 * group 1 must not be empty, so it would be instead styled as PR_TAG by
773 * the generic tag rule. The handler registered for the 'js' extension would
774 * then be called with 'foo()', and finally, the current decorator would
775 * be called with '<\/script>' which would not match the original rule and
776 * so the generic tag rule would identify it as a tag.
778 * Pattern must only match prefixes, and if it matches a prefix, then that
779 * match is considered a token with the same style.
781 * Context is applied to the last non-whitespace, non-comment token
784 * Shortcut is an optional string of characters, any of which, if the first
785 * character, gurantee that this pattern and only this pattern matches.
787 * @param {Array} shortcutStylePatterns patterns that always start with
788 * a known character. Must have a shortcut string.
789 * @param {Array} fallthroughStylePatterns patterns that will be tried in
790 * order if the shortcut ones fail. May have shortcuts.
792 * @return {function (Object)} a
793 * function that takes source code and returns a list of decorations.
795 function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
799 var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
802 for (var i = 0, n = allPatterns.length; i < n; ++i) {
803 var patternParts = allPatterns[i];
804 var shortcutChars = patternParts[3];
806 for (var c = shortcutChars.length; --c >= 0;) {
807 shortcuts[shortcutChars.charAt(c)] = patternParts;
810 var regex = patternParts[1];
812 if (!regexKeys.hasOwnProperty(k)) {
813 allRegexs.push(regex);
817 allRegexs.push(/[\0-\uffff]/);
818 tokenizer = combinePrefixPatterns(allRegexs);
821 var nPatterns = fallthroughStylePatterns.length;
824 * Lexes job.sourceCode and produces an output array job.decorations of
825 * style classes preceded by the position at which they start in
826 * job.sourceCode in order.
828 * @param {Object} job an object like <pre>{
829 * sourceCode: {string} sourceText plain text,
830 * basePos: {int} position of job.sourceCode in the larger chunk of
834 var decorate = function (job) {
835 var sourceCode = job.sourceCode, basePos = job.basePos;
836 /** Even entries are positions in source in ascending order. Odd enties
837 * are style markers (e.g., PR_COMMENT) that run from that position until
839 * @type {Array.<number|string>}
841 var decorations = [basePos, PR_PLAIN];
842 var pos = 0; // index into sourceCode
843 var tokens = sourceCode.match(tokenizer) || [];
846 for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
847 var token = tokens[ti];
848 var style = styleCache[token];
852 if (typeof style === 'string') {
855 var patternParts = shortcuts[token.charAt(0)];
857 match = token.match(patternParts[1]);
858 style = patternParts[0];
860 for (var i = 0; i < nPatterns; ++i) {
861 patternParts = fallthroughStylePatterns[i];
862 match = token.match(patternParts[1]);
864 style = patternParts[0];
869 if (!match) { // make sure that we make progress
874 isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
875 if (isEmbedded && !(match && typeof match[1] === 'string')) {
880 if (!isEmbedded) { styleCache[token] = style; }
883 var tokenStart = pos;
887 decorations.push(basePos + tokenStart, style);
888 } else { // Treat group 1 as an embedded block of source code.
889 var embeddedSource = match[1];
890 var embeddedSourceStart = token.indexOf(embeddedSource);
891 var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
893 // If embeddedSource can be blank, then it would match at the
894 // beginning which would cause us to infinitely recurse on the
895 // entire token, so we catch the right context in match[2].
896 embeddedSourceEnd = token.length - match[2].length;
897 embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
899 var lang = style.substring(5);
900 // Decorate the left of the embedded source
902 basePos + tokenStart,
903 token.substring(0, embeddedSourceStart),
904 decorate, decorations);
905 // Decorate the embedded source
907 basePos + tokenStart + embeddedSourceStart,
909 langHandlerForExtension(lang, embeddedSource),
911 // Decorate the right of the embedded section
913 basePos + tokenStart + embeddedSourceEnd,
914 token.substring(embeddedSourceEnd),
915 decorate, decorations);
918 job.decorations = decorations;
923 /** returns a function that produces a list of decorations from source text.
925 * This code treats ", ', and ` as string delimiters, and \ as a string
926 * escape. It does not recognize perl's qq() style strings.
927 * It has no special handling for double delimiter escapes as in basic, or
928 * the tripled delimiters used in python, but should work on those regardless
929 * although in those cases a single string literal may be broken up into
930 * multiple adjacent string literals.
932 * It recognizes C, C++, and shell style comments.
934 * @param {Object} options a set of optional parameters.
935 * @return {function (Object)} a function that examines the source code
936 * in the input job and builds the decoration list.
938 function sourceDecorator(options) {
939 var shortcutStylePatterns = [], fallthroughStylePatterns = [];
940 if (options['tripleQuotedStrings']) {
941 // '''multi-line-string''', 'single-line-string', and double-quoted
942 shortcutStylePatterns.push(
943 [PR_STRING, /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
945 } else if (options['multiLineStrings']) {
946 // 'multi-line-string', "multi-line-string"
947 shortcutStylePatterns.push(
948 [PR_STRING, /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
951 // 'single-line-string', "single-line-string"
952 shortcutStylePatterns.push(
954 /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
957 if (options['verbatimStrings']) {
958 // verbatim-string-literal production from the C# grammar. See issue 93.
959 fallthroughStylePatterns.push(
960 [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
962 var hc = options['hashComments'];
964 if (options['cStyleComments']) {
965 if (hc > 1) { // multiline hash comments
966 shortcutStylePatterns.push(
967 [PR_COMMENT, /^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/, null, '#']);
969 // Stop C preprocessor declarations at an unclosed open comment
970 shortcutStylePatterns.push(
971 [PR_COMMENT, /^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\b|[^\r\n]*)/,
974 // #include <stdio.h>
975 fallthroughStylePatterns.push(
977 /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h(?:h|pp|\+\+)?|[a-z]\w*)>/,
980 shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
983 if (options['cStyleComments']) {
984 fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
985 fallthroughStylePatterns.push(
986 [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
988 var regexLiterals = options['regexLiterals'];
993 var regexExcls = regexLiterals > 1
994 ? '' // Multiline regex literals
999 var regexAny = regexExcls ? '.' : '[\\S\\s]';
1003 var REGEX_LITERAL = (
1004 // A regular expression literal starts with a slash that is
1005 // not followed by * or / so that it is not confused with
1007 '/(?=[^/*' + regexExcls + '])'
1008 // and then contains any number of raw characters,
1009 + '(?:[^/\\x5B\\x5C' + regexExcls + ']'
1010 // escape sequences (\x5C),
1011 + '|\\x5C' + regexAny
1012 // or non-nesting character sets (\x5B\x5D);
1013 + '|\\x5B(?:[^\\x5C\\x5D' + regexExcls + ']'
1014 + '|\\x5C' + regexAny + ')*(?:\\x5D|$))+'
1015 // finally closed by a /.
1017 fallthroughStylePatterns.push(
1019 RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
1023 var types = options['types'];
1025 fallthroughStylePatterns.push([PR_TYPE, types]);
1028 var keywords = ("" + options['keywords']).replace(/^ | $/g, '');
1029 if (keywords.length) {
1030 fallthroughStylePatterns.push(
1032 new RegExp('^(?:' + keywords.replace(/[\s,]+/g, '|') + ')\\b'),
1036 shortcutStylePatterns.push([PR_PLAIN, /^\s+/, null, ' \r\n\t\xA0']);
1039 // The Bash man page says
1041 // A word is a sequence of characters considered as a single
1042 // unit by GRUB. Words are separated by metacharacters,
1043 // which are the following plus space, tab, and newline: { }
1047 // A word beginning with # causes that word and all remaining
1048 // characters on that line to be ignored.
1050 // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
1051 // comment but empirically
1059 // so /(?:^|[|&;<>\s])/ is more appropriate.
1061 // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
1062 // suggests that this definition is compatible with a
1063 // default mode that tries to use a single token definition
1064 // to recognize both bash/python style comments and C
1065 // preprocessor directives.
1067 // This definition of punctuation does not include # in the list of
1068 // follow-on exclusions, so # will not be broken before if preceeded
1069 // by a punctuation character. We could try to exclude # after
1070 // [|&;<>] but that doesn't seem to cause many major problems.
1071 // If that does turn out to be a problem, we should change the below
1072 // when hc is truthy to include # in the run of punctuation characters
1073 // only when not followint [|&;<>].
1074 '^.[^\\s\\w.$@\'"`/\\\\]*';
1075 if (options['regexLiterals']) {
1076 punctuation += '(?!\s*\/)';
1079 fallthroughStylePatterns.push(
1080 // TODO(mikesamuel): recognize non-latin letters and numerals in idents
1081 [PR_LITERAL, /^@[a-z_$][a-z_$@0-9]*/i, null],
1082 [PR_TYPE, /^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/, null],
1083 [PR_PLAIN, /^[a-z_$][a-z_$@0-9]*/i, null],
1089 // or an octal or decimal number,
1090 + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
1091 // possibly in scientific notation
1092 + '(?:e[+\\-]?\\d+)?'
1094 // with an optional modifier like UL for unsigned long
1096 null, '0123456789'],
1097 // Don't treat escaped quotes in bash as starting strings.
1099 [PR_PLAIN, /^\\[\s\S]?/, null],
1100 [PR_PUNCTUATION, new RegExp(punctuation), null]);
1102 return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
1105 var decorateSource = sourceDecorator({
1106 'keywords': ALL_KEYWORDS,
1107 'hashComments': true,
1108 'cStyleComments': true,
1109 'multiLineStrings': true,
1110 'regexLiterals': true
1114 * Given a DOM subtree, wraps it in a list, and puts each line into its own
1117 * @param {Node} node modified in place. Its content is pulled into an
1118 * HTMLOListElement, and each line is moved into a separate list item.
1119 * This requires cloning elements, so the input might not have unique
1120 * IDs after numbering.
1121 * @param {boolean} isPreformatted true iff white-space in text nodes should
1122 * be treated as significant.
1124 function numberLines(node, opt_startLineNum, isPreformatted) {
1125 var nocode = /(?:^|\s)nocode(?:\s|$)/;
1126 var lineBreak = /\r\n?|\n/;
1128 var document = node.ownerDocument;
1130 var li = document.createElement('li');
1131 while (node.firstChild) {
1132 li.appendChild(node.firstChild);
1134 // An array of lines. We split below, so this is initialized to one
1136 var listItems = [li];
1138 function walk(node) {
1139 var type = node.nodeType;
1140 if (type == 1 && !nocode.test(node.className)) { // Element
1141 if ('br' === node.nodeName) {
1143 // Discard the <BR> since it is now flush against a </LI>.
1144 if (node.parentNode) {
1145 node.parentNode.removeChild(node);
1148 for (var child = node.firstChild; child; child = child.nextSibling) {
1152 } else if ((type == 3 || type == 4) && isPreformatted) { // Text
1153 var text = node.nodeValue;
1154 var match = text.match(lineBreak);
1156 var firstLine = text.substring(0, match.index);
1157 node.nodeValue = firstLine;
1158 var tail = text.substring(match.index + match[0].length);
1160 var parent = node.parentNode;
1161 parent.insertBefore(
1162 document.createTextNode(tail), node.nextSibling);
1166 // Don't leave blank text nodes in the DOM.
1167 node.parentNode.removeChild(node);
1173 // Split a line after the given node.
1174 function breakAfter(lineEndNode) {
1175 // If there's nothing to the right, then we can skip ending the line
1176 // here, and move root-wards since splitting just before an end-tag
1177 // would require us to create a bunch of empty copies.
1178 while (!lineEndNode.nextSibling) {
1179 lineEndNode = lineEndNode.parentNode;
1180 if (!lineEndNode) { return; }
1183 function breakLeftOf(limit, copy) {
1184 // Clone shallowly if this node needs to be on both sides of the break.
1185 var rightSide = copy ? limit.cloneNode(false) : limit;
1186 var parent = limit.parentNode;
1188 // We clone the parent chain.
1189 // This helps us resurrect important styling elements that cross lines.
1190 // E.g. in <i>Foo<br>Bar</i>
1191 // should be rewritten to <li><i>Foo</i></li><li><i>Bar</i></li>.
1192 var parentClone = breakLeftOf(parent, 1);
1193 // Move the clone and everything to the right of the original
1194 // onto the cloned parent.
1195 var next = limit.nextSibling;
1196 parentClone.appendChild(rightSide);
1197 for (var sibling = next; sibling; sibling = next) {
1198 next = sibling.nextSibling;
1199 parentClone.appendChild(sibling);
1205 var copiedListItem = breakLeftOf(lineEndNode.nextSibling, 0);
1207 // Walk the parent chain until we reach an unattached LI.
1209 // Check nodeType since IE invents document fragments.
1210 (parent = copiedListItem.parentNode) && parent.nodeType === 1;) {
1211 copiedListItem = parent;
1213 // Put it on the list of lines for later processing.
1214 listItems.push(copiedListItem);
1217 // Split lines while there are lines left to split.
1218 for (var i = 0; // Number of lines that have been split so far.
1219 i < listItems.length; // length updated by breakAfter calls.
1224 // Make sure numeric indices show correctly.
1225 if (opt_startLineNum === (opt_startLineNum|0)) {
1226 listItems[0].setAttribute('value', opt_startLineNum);
1229 var ol = document.createElement('ol');
1230 ol.className = 'linenums';
1231 var offset = Math.max(0, ((opt_startLineNum - 1 /* zero index */)) | 0) || 0;
1232 for (var i = 0, n = listItems.length; i < n; ++i) {
1234 // Stick a class on the LIs so that stylesheets can
1235 // color odd/even rows, or any other row pattern that
1236 // is co-prime with 10.
1237 li.className = 'L' + ((i + offset) % 10);
1238 if (!li.firstChild) {
1239 li.appendChild(document.createTextNode('\xA0'));
1244 node.appendChild(ol);
1247 * Breaks {@code job.sourceCode} around style boundaries in
1248 * {@code job.decorations} and modifies {@code job.sourceNode} in place.
1249 * @param {Object} job like <pre>{
1250 * sourceCode: {string} source as plain text,
1251 * sourceNode: {HTMLElement} the element containing the source,
1252 * spans: {Array.<number|Node>} alternating span start indices into source
1253 * and the text node or element (e.g. {@code <BR>}) corresponding to that
1255 * decorations: {Array.<number|string} an array of style classes preceded
1256 * by the position at which they start in job.sourceCode in order
1260 function recombineTagsAndDecorations(job) {
1261 var isIE8OrEarlier = /\bMSIE\s(\d+)/.exec(navigator.userAgent);
1262 isIE8OrEarlier = isIE8OrEarlier && +isIE8OrEarlier[1] <= 8;
1263 var newlineRe = /\n/g;
1265 var source = job.sourceCode;
1266 var sourceLength = source.length;
1267 // Index into source after the last code-unit recombined.
1268 var sourceIndex = 0;
1270 var spans = job.spans;
1271 var nSpans = spans.length;
1272 // Index into spans after the last span which ends at or before sourceIndex.
1275 var decorations = job.decorations;
1276 var nDecorations = decorations.length;
1277 // Index into decorations after the last decoration which ends at or before
1279 var decorationIndex = 0;
1281 // Remove all zero-length decorations.
1282 decorations[nDecorations] = sourceLength;
1284 for (i = decPos = 0; i < nDecorations;) {
1285 if (decorations[i] !== decorations[i + 2]) {
1286 decorations[decPos++] = decorations[i++];
1287 decorations[decPos++] = decorations[i++];
1292 nDecorations = decPos;
1294 // Simplify decorations.
1295 for (i = decPos = 0; i < nDecorations;) {
1296 var startPos = decorations[i];
1297 // Conflate all adjacent decorations that use the same style.
1298 var startDec = decorations[i + 1];
1300 while (end + 2 <= nDecorations && decorations[end + 1] === startDec) {
1303 decorations[decPos++] = startPos;
1304 decorations[decPos++] = startDec;
1308 nDecorations = decorations.length = decPos;
1310 var sourceNode = job.sourceNode;
1313 oldDisplay = sourceNode.style.display;
1314 sourceNode.style.display = 'none';
1317 var decoration = null;
1318 while (spanIndex < nSpans) {
1319 var spanStart = spans[spanIndex];
1320 var spanEnd = spans[spanIndex + 2] || sourceLength;
1322 var decEnd = decorations[decorationIndex + 2] || sourceLength;
1324 var end = Math.min(spanEnd, decEnd);
1326 var textNode = spans[spanIndex + 1];
1328 if (textNode.nodeType !== 1 // Don't muck with <BR>s or <LI>s
1329 // Don't introduce spans around empty text nodes.
1330 && (styledText = source.substring(sourceIndex, end))) {
1331 // This may seem bizarre, and it is. Emitting LF on IE causes the
1332 // code to display with spaces instead of line breaks.
1333 // Emitting Windows standard issue linebreaks (CRLF) causes a blank
1334 // space to appear at the beginning of every line but the first.
1335 // Emitting an old Mac OS 9 line separator makes everything spiffy.
1336 if (isIE8OrEarlier) {
1337 styledText = styledText.replace(newlineRe, '\r');
1339 textNode.nodeValue = styledText;
1340 var document = textNode.ownerDocument;
1341 var span = document.createElement('span');
1342 span.className = decorations[decorationIndex + 1];
1343 var parentNode = textNode.parentNode;
1344 parentNode.replaceChild(span, textNode);
1345 span.appendChild(textNode);
1346 if (sourceIndex < spanEnd) { // Split off a text node.
1347 spans[spanIndex + 1] = textNode
1348 // TODO: Possibly optimize by using '' if there's no flicker.
1349 = document.createTextNode(source.substring(end, spanEnd));
1350 parentNode.insertBefore(textNode, span.nextSibling);
1356 if (sourceIndex >= spanEnd) {
1359 if (sourceIndex >= decEnd) {
1360 decorationIndex += 2;
1365 sourceNode.style.display = oldDisplay;
1370 /** Maps language-specific file extensions to handlers. */
1371 var langHandlerRegistry = {};
1372 /** Register a language handler for the given file extensions.
1373 * @param {function (Object)} handler a function from source code to a list
1374 * of decorations. Takes a single argument job which describes the
1375 * state of the computation. The single parameter has the form
1377 * sourceCode: {string} as plain text.
1378 * decorations: {Array.<number|string>} an array of style classes
1379 * preceded by the position at which they start in
1380 * job.sourceCode in order.
1381 * The language handler should assigned this field.
1382 * basePos: {int} the position of source in the larger source chunk.
1383 * All positions in the output decorations array are relative
1384 * to the larger source chunk.
1386 * @param {Array.<string>} fileExtensions
1388 function registerLangHandler(handler, fileExtensions) {
1389 for (var i = fileExtensions.length; --i >= 0;) {
1390 var ext = fileExtensions[i];
1391 if (!langHandlerRegistry.hasOwnProperty(ext)) {
1392 langHandlerRegistry[ext] = handler;
1393 } else if (win['console']) {
1394 console['warn']('cannot override language handler %s', ext);
1398 function langHandlerForExtension(extension, source) {
1399 if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
1400 // Treat it as markup if the first non whitespace character is a < and
1401 // the last non-whitespace character is a >.
1402 extension = /^\s*</.test(source)
1406 return langHandlerRegistry[extension];
1408 registerLangHandler(decorateSource, ['default-code']);
1409 registerLangHandler(
1413 [PR_PLAIN, /^[^<?]+/],
1414 [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
1415 [PR_COMMENT, /^<\!--[\s\S]*?(?:-\->|$)/],
1416 // Unescaped content in an unknown language
1417 ['lang-', /^<\?([\s\S]+?)(?:\?>|$)/],
1418 ['lang-', /^<%([\s\S]+?)(?:%>|$)/],
1419 [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
1420 ['lang-', /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
1421 // Unescaped content in javascript. (Or possibly vbscript).
1422 ['lang-js', /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
1423 // Contains unescaped stylesheet content
1424 ['lang-css', /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
1425 ['lang-in.tag', /^(<\/?[a-z][^<>]*>)/i]
1427 ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
1428 registerLangHandler(
1431 [PR_PLAIN, /^[\s]+/, null, ' \t\r\n'],
1432 [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
1435 [PR_TAG, /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
1436 [PR_ATTRIB_NAME, /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
1437 ['lang-uq.val', /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
1438 [PR_PUNCTUATION, /^[=<>\/]+/],
1439 ['lang-js', /^on\w+\s*=\s*\"([^\"]+)\"/i],
1440 ['lang-js', /^on\w+\s*=\s*\'([^\']+)\'/i],
1441 ['lang-js', /^on\w+\s*=\s*([^\"\'>\s]+)/i],
1442 ['lang-css', /^style\s*=\s*\"([^\"]+)\"/i],
1443 ['lang-css', /^style\s*=\s*\'([^\']+)\'/i],
1444 ['lang-css', /^style\s*=\s*([^\"\'>\s]+)/i]
1447 registerLangHandler(
1448 createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
1449 registerLangHandler(sourceDecorator({
1450 'keywords': CPP_KEYWORDS,
1451 'hashComments': true,
1452 'cStyleComments': true,
1454 }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
1455 registerLangHandler(sourceDecorator({
1456 'keywords': 'null,true,false'
1458 registerLangHandler(sourceDecorator({
1459 'keywords': CSHARP_KEYWORDS,
1460 'hashComments': true,
1461 'cStyleComments': true,
1462 'verbatimStrings': true,
1465 registerLangHandler(sourceDecorator({
1466 'keywords': JAVA_KEYWORDS,
1467 'cStyleComments': true
1469 registerLangHandler(sourceDecorator({
1470 'keywords': SH_KEYWORDS,
1471 'hashComments': true,
1472 'multiLineStrings': true
1473 }), ['bash', 'bsh', 'csh', 'sh']);
1474 registerLangHandler(sourceDecorator({
1475 'keywords': PYTHON_KEYWORDS,
1476 'hashComments': true,
1477 'multiLineStrings': true,
1478 'tripleQuotedStrings': true
1479 }), ['cv', 'py', 'python']);
1480 registerLangHandler(sourceDecorator({
1481 'keywords': PERL_KEYWORDS,
1482 'hashComments': true,
1483 'multiLineStrings': true,
1484 'regexLiterals': 2 // multiline regex literals
1485 }), ['perl', 'pl', 'pm']);
1486 registerLangHandler(sourceDecorator({
1487 'keywords': RUBY_KEYWORDS,
1488 'hashComments': true,
1489 'multiLineStrings': true,
1490 'regexLiterals': true
1491 }), ['rb', 'ruby']);
1492 registerLangHandler(sourceDecorator({
1493 'keywords': JSCRIPT_KEYWORDS,
1494 'cStyleComments': true,
1495 'regexLiterals': true
1496 }), ['javascript', 'js']);
1497 registerLangHandler(sourceDecorator({
1498 'keywords': COFFEE_KEYWORDS,
1499 'hashComments': 3, // ### style block comments
1500 'cStyleComments': true,
1501 'multilineStrings': true,
1502 'tripleQuotedStrings': true,
1503 'regexLiterals': true
1505 registerLangHandler(sourceDecorator({
1506 'keywords': RUST_KEYWORDS,
1507 'cStyleComments': true,
1508 'multilineStrings': true
1509 }), ['rc', 'rs', 'rust']);
1510 registerLangHandler(
1511 createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
1513 function applyDecorator(job) {
1514 var opt_langExtension = job.langExtension;
1517 // Extract tags, and convert the source code to plain text.
1518 var sourceAndSpans = extractSourceSpans(job.sourceNode, job.pre);
1519 /** Plain text. @type {string} */
1520 var source = sourceAndSpans.sourceCode;
1521 job.sourceCode = source;
1522 job.spans = sourceAndSpans.spans;
1525 // Apply the appropriate language handler
1526 langHandlerForExtension(opt_langExtension, source)(job);
1528 // Integrate the decorations and tags back into the source code,
1529 // modifying the sourceNode in place.
1530 recombineTagsAndDecorations(job);
1532 if (win['console']) {
1533 console['log'](e && e['stack'] || e);
1539 * Pretty print a chunk of code.
1540 * @param sourceCodeHtml {string} The HTML to pretty print.
1541 * @param opt_langExtension {string} The language name to use.
1542 * Typically, a filename extension like 'cpp' or 'java'.
1543 * @param opt_numberLines {number|boolean} True to number lines,
1544 * or the 1-indexed number of the first line in sourceCodeHtml.
1546 function $prettyPrintOne(sourceCodeHtml, opt_langExtension, opt_numberLines) {
1547 var container = document.createElement('div');
1548 // This could cause images to load and onload listeners to fire.
1549 // E.g. <img onerror="alert(1337)" src="nosuchimage.png">.
1550 // We assume that the inner HTML is from a trusted source.
1551 // The pre-tag is required for IE8 which strips newlines from innerHTML
1552 // when it is injected into a <pre> tag.
1553 // http://stackoverflow.com/questions/451486/pre-tag-loses-line-breaks-when-setting-innerhtml-in-ie
1554 // http://stackoverflow.com/questions/195363/inserting-a-newline-into-a-pre-tag-ie-javascript
1555 container.innerHTML = '<pre>' + sourceCodeHtml + '</pre>';
1556 container = container.firstChild;
1557 if (opt_numberLines) {
1558 numberLines(container, opt_numberLines, true);
1562 langExtension: opt_langExtension,
1563 numberLines: opt_numberLines,
1564 sourceNode: container,
1567 applyDecorator(job);
1568 return container.innerHTML;
1572 * Find all the {@code <pre>} and {@code <code>} tags in the DOM with
1573 * {@code class=prettyprint} and prettify them.
1575 * @param {Function} opt_whenDone called when prettifying is done.
1576 * @param {HTMLElement|HTMLDocument} opt_root an element or document
1577 * containing all the elements to pretty print.
1578 * Defaults to {@code document.body}.
1580 function $prettyPrint(opt_whenDone, opt_root) {
1581 var root = opt_root || document.body;
1582 var doc = root.ownerDocument || document;
1583 function byTagName(tn) { return root.getElementsByTagName(tn); }
1584 // fetch a list of nodes to rewrite
1585 var codeSegments = [byTagName('pre'), byTagName('code'), byTagName('xmp')];
1587 for (var i = 0; i < codeSegments.length; ++i) {
1588 for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
1589 elements.push(codeSegments[i][j]);
1592 codeSegments = null;
1595 if (!clock['now']) {
1596 clock = { 'now': function () { return +(new Date); } };
1599 // The loop is broken into a series of continuations to make sure that we
1600 // don't make the browser unresponsive when rewriting a large page.
1602 var prettyPrintingJob;
1604 var langExtensionRe = /\blang(?:uage)?-([\w.]+)(?!\S)/;
1605 var prettyPrintRe = /\bprettyprint\b/;
1606 var prettyPrintedRe = /\bprettyprinted\b/;
1607 var preformattedTagNameRe = /pre|xmp/i;
1608 var codeRe = /^code$/i;
1609 var preCodeXmpRe = /^(?:pre|code|xmp)$/i;
1613 var endTime = (win['PR_SHOULD_USE_CONTINUATION'] ?
1614 clock['now']() + 250 /* ms */ :
1616 for (; k < elements.length && clock['now']() < endTime; k++) {
1617 var cs = elements[k];
1619 // Look for a preceding comment like
1620 // <?prettify lang="..." linenums="..."?>
1623 for (var preceder = cs; (preceder = preceder.previousSibling);) {
1624 var nt = preceder.nodeType;
1625 // <?foo?> is parsed by HTML 5 to a comment node (8)
1626 // like <!--?foo?-->, but in XML is a processing instruction
1627 var value = (nt === 7 || nt === 8) && preceder.nodeValue;
1629 ? !/^\??prettify\b/.test(value)
1630 : (nt !== 3 || /\S/.test(preceder.nodeValue))) {
1631 // Skip over white-space text nodes but not others.
1637 /\b(\w+)=([\w:.%+-]+)/g,
1638 function (_, name, value) { attrs[name] = value; });
1644 var className = cs.className;
1645 if ((attrs !== EMPTY || prettyPrintRe.test(className))
1646 // Don't redo this if we've already done it.
1647 // This allows recalling pretty print to just prettyprint elements
1648 // that have been added to the page since last call.
1649 && !prettyPrintedRe.test(className)) {
1651 // make sure this is not nested in an already prettified element
1653 for (var p = cs.parentNode; p; p = p.parentNode) {
1655 if (preCodeXmpRe.test(tn)
1656 && p.className && prettyPrintRe.test(p.className)) {
1662 // Mark done. If we fail to prettyprint for whatever reason,
1663 // we shouldn't try again.
1664 cs.className += ' prettyprinted';
1666 // If the classes includes a language extensions, use it.
1667 // Language extensions can be specified like
1668 // <pre class="prettyprint lang-cpp">
1669 // the language extension "cpp" is used to find a language handler
1670 // as passed to PR.registerLangHandler.
1671 // HTML5 recommends that a language be specified using "language-"
1672 // as the prefix instead. Google Code Prettify supports both.
1673 // http://dev.w3.org/html5/spec-author-view/the-code-element.html
1674 var langExtension = attrs['lang'];
1675 if (!langExtension) {
1676 langExtension = className.match(langExtensionRe);
1677 // Support <pre class="prettyprint"><code class="language-c">
1679 if (!langExtension && (wrapper = childContentWrapper(cs))
1680 && codeRe.test(wrapper.tagName)) {
1681 langExtension = wrapper.className.match(langExtensionRe);
1684 if (langExtension) { langExtension = langExtension[1]; }
1688 if (preformattedTagNameRe.test(cs.tagName)) {
1691 var currentStyle = cs['currentStyle'];
1692 var defaultView = doc.defaultView;
1695 ? currentStyle['whiteSpace']
1697 && defaultView.getComputedStyle)
1698 ? defaultView.getComputedStyle(cs, null)
1699 .getPropertyValue('white-space')
1701 preformatted = whitespace
1702 && 'pre' === whitespace.substring(0, 3);
1705 // Look for a class like linenums or linenums:<n> where <n> is the
1706 // 1-indexed number of the first line.
1707 var lineNums = attrs['linenums'];
1708 if (!(lineNums = lineNums === 'true' || +lineNums)) {
1709 lineNums = className.match(/\blinenums\b(?::(\d+))?/);
1712 ? lineNums[1] && lineNums[1].length
1713 ? +lineNums[1] : true
1716 if (lineNums) { numberLines(cs, lineNums, preformatted); }
1718 // do the pretty printing
1719 prettyPrintingJob = {
1720 langExtension: langExtension,
1722 numberLines: lineNums,
1725 applyDecorator(prettyPrintingJob);
1729 if (k < elements.length) {
1730 // finish up in a continuation
1731 setTimeout(doWork, 250);
1732 } else if ('function' === typeof opt_whenDone) {
1741 * Contains functions for creating and registering new language handlers.
1744 var PR = win['PR'] = {
1745 'createSimpleLexer': createSimpleLexer,
1746 'registerLangHandler': registerLangHandler,
1747 'sourceDecorator': sourceDecorator,
1748 'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
1749 'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
1750 'PR_COMMENT': PR_COMMENT,
1751 'PR_DECLARATION': PR_DECLARATION,
1752 'PR_KEYWORD': PR_KEYWORD,
1753 'PR_LITERAL': PR_LITERAL,
1754 'PR_NOCODE': PR_NOCODE,
1755 'PR_PLAIN': PR_PLAIN,
1756 'PR_PUNCTUATION': PR_PUNCTUATION,
1757 'PR_SOURCE': PR_SOURCE,
1758 'PR_STRING': PR_STRING,
1763 ? (win['prettyPrintOne'] = $prettyPrintOne)
1764 : (prettyPrintOne = $prettyPrintOne),
1765 'prettyPrint': prettyPrint =
1767 ? (win['prettyPrint'] = $prettyPrint)
1768 : (prettyPrint = $prettyPrint)
1771 // Make PR available via the Asynchronous Module Definition (AMD) API.
1772 // Per https://github.com/amdjs/amdjs-api/wiki/AMD:
1773 // The Asynchronous Module Definition (AMD) API specifies a
1774 // mechanism for defining modules such that the module and its
1775 // dependencies can be asynchronously loaded.
1777 // To allow a clear indicator that a global define function (as
1778 // needed for script src browser loading) conforms to the AMD API,
1779 // any global define function SHOULD have a property called "amd"
1780 // whose value is an object. This helps avoid conflict with any
1781 // other existing JavaScript code that could have defined a define()
1782 // function that does not conform to the AMD API.
1783 if (typeof define === "function" && define['amd']) {
1784 define("google-code-prettify", [], function () {
1789 })( window, window.angular );